{
  "openapi": "3.0.3",
  "info": {
    "title": "InterServer Management API",
    "version": "0.9.0",
    "x-service-info": {
      "category": "web-infrastructure",
      "subcategories": [
        "hosting",
        "cloud-compute",
        "domains-dns",
        "email-delivery",
        "tls-certificates",
        "storage-backup",
        "networking"
      ],
      "supported_currencies": [
        "USD"
      ],
      "billing_models": [
        "one-time",
        "recurring"
      ],
      "marketplace": {
        "url": "https://www.interserver.net/",
        "order_endpoint": "/billing/cart",
        "checkout_endpoint": "/billing/pay/{method}/{invoices}"
      }
    },
    "description": "# Overview\n\nThe InterServer Management API provides programmatic access to manage your InterServer services. Use this REST API to automate provisioning, configuration, and billing operations across your account.\n\nThe API covers the following service categories:\n- [Domains](https://www.interserver.net/domains/) — registration, transfers, and DNS management\n- [Web Hosting](https://www.interserver.net/hosting/) — shared and reseller hosting\n- [VPS Hosting](https://www.interserver.net/vps/) — virtual private servers\n- [Dedicated Servers](https://www.interserver.net/dedicated/) — bare metal and [Rapid Deploy Servers](https://www.interserver.net/dedicated/rapid-deploy.html)\n- [Backups](https://www.interserver.net/storage/) — storage and backup services\n- Licenses — control panel and software licenses\n- [Mail](https://www.mail.baby/) — mail delivery services\n- SSL — certificate provisioning\n- Billing — invoices, payment methods, and account management\n\nFor interactive testing, see the [API documentation](/api-docs/).\n\n# Authentication\n\nMost endpoints require authentication. Two methods are supported:\n\n## API Key (Preferred)\n\nGenerate an API key from the [Account Security](https://my.interserver.net/account_security) page on [my.interserver.net](https://my.interserver.net/). Pass it in the `X-API-KEY` request header:\n\n```\nX-API-KEY: your-api-key-here\n```\n\n## Session-Based Authentication\n\nAlternatively, authenticate by creating a session:\n\n1. **Log in** — Send a `POST` request to `/login` with your account credentials. The response includes a session identifier.\n2. **Pass the session ID** — Include the session identifier in the `sessionid` header on subsequent requests:\n\n```\nsessionid: your-session-id-here\n```\n\nAPI key authentication is recommended for most integrations as it does not expire and avoids the overhead of session management.\n\n",
    "termsOfService": "https://www.interserver.net/terms-of-service.html",
    "contact": {
      "name": "InterServer Support Staff",
      "url": "https://www.interserver.net/contact-us.html",
      "email": "support@interserver.net"
    },
    "license": {
      "name": "MIT License",
      "url": "https://opensource.org/licenses/MIT"
    }
  },
  "servers": [
    {
      "url": "https://my.interserver.net/apiv2",
      "description": "Live API Endpoint"
    }
  ],
  "security": [
    {
      "apiKeyAuth": []
    },
    {
      "sessionIdHeaderAuth": []
    },
    {
      "sessionIdCookieAuth": []
    }
  ],
  "tags": [
    {
      "name": "Public",
      "description": "Endpoints that do not require authentication. Use these to check API availability, look up reference data (countries, currencies, timezones), and authenticate."
    },
    {
      "name": "Account",
      "description": "Manage your account profile, contact information, security settings, API keys, SSH keys, two-factor authentication, and IP access restrictions."
    },
    {
      "name": "Billing",
      "description": "Invoices, shopping cart, prepay balances, credit cards, and payment processing. Use `/billing/pay/{method}/{invoices}` to complete payment for outstanding invoices."
    },
    {
      "name": "Backups",
      "description": "Cloud backup storage services. Order, view, and manage backup storage quotas and credentials."
    },
    {
      "name": "DNS",
      "description": "Manage DNS domains and records hosted on InterServer's nameservers. Create domains, add/update/delete A, AAAA, CNAME, MX, TXT, and other record types."
    },
    {
      "name": "Domains",
      "description": "Domain registration, transfers, renewals, WHOIS, nameserver management, and DNSSEC configuration."
    },
    {
      "name": "Floating_IPs",
      "description": "Floating IP services that provide portable IP addresses which can be reassigned between servers for high availability and failover."
    },
    {
      "name": "Licenses",
      "description": "Software license provisioning for control panels (cPanel, Plesk, DirectAdmin) and other licensed software. Order, manage, and change IP assignments."
    },
    {
      "name": "Mail",
      "description": "Mail Baby email delivery services. Order, manage, view logs, configure alerts and deny rules, and monitor deliverability."
    },
    {
      "name": "QuickServers",
      "description": "Rapid Deploy Servers (QuickServers) — pre-configured dedicated servers available for fast provisioning with OS reinstall, backup, and VNC capabilities."
    },
    {
      "name": "Servers",
      "description": "Dedicated server ordering and management. Configure custom hardware, view IPMI information, and manage server lifecycle."
    },
    {
      "name": "SSL-Certificates",
      "description": "SSL/TLS certificate provisioning and management. Order, renew, and manage certificates for securing your domains."
    },
    {
      "name": "VPS",
      "description": "Virtual Private Server management. Order, configure, backup, restore, reinstall, and control VPS instances with full root access."
    },
    {
      "name": "Webhosting",
      "description": "Shared and reseller web hosting packages. Order, manage, and access hosting control panels, backups, and reverse DNS."
    },
    {
      "name": "Tickets",
      "description": "Support ticket system. Create new tickets, reply to existing ones, view ticket history, and close resolved issues."
    },
    {
      "name": "Scrub Ips",
      "description": "DDoS protection and IP scrubbing services. Manage protected IPs, configure firewall rules, geographic filters, and view traffic logs."
    }
  ],
  "externalDocs": {
    "description": "Tips",
    "url": "https://www.interserver.net/tips/"
  },
  "paths": {
    "/account": {
      "summary": "User Account Information and Management",
      "description": "This endpoint allows you to manage various user details, including contact information and account security.\n\n- Use the `GET` method to retrieve your account information.\n- Use the `POST` method to update your stored contact and billing information.\n",
      "get": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountInfo"
                }
              }
            },
            "links": {
              "UpdateAccountInfo": {
                "operationId": "updateAccountInfo",
                "description": "Use the returned account data to pre-fill an update form and submit changes via POST /account."
              }
            },
            "description": "Your account information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAccountInfo",
        "summary": "Read full account profile, billing address, and security settings",
        "description": "Use to render the account-settings page or to verify current state before mutating with `updateAccountInfo`. No body, no path params. Returns: full profile (name, company, address1/2, city, state, zip, country, phone, email_invoices, email_abuse, gstin, locale, timezone), masked credit-card list (last-4 digits only — full PAN never returned), OAuth provider config (with secret keys stripped), feature toggles (`disable_reset`, `disable_reinstall`, `disable_*_notifications`), gravatar URL, language, country->currency map, and `enableLocales`/`enableCurrencies` UI flags. Timezone defaults to IP-derived value if unset, falling back to America/New_York. Errors: 401 if session invalid or expired. Sibling ops: `updateAccountInfo`, `getAccountTfaSetup`, `updateAccountFeatures`, `updateAccountIpLimits`."
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AccountInfoPost"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountInfoPost"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/AccountValidationError"
          }
        },
        "operationId": "updateAccountInfo",
        "summary": "Update contact and billing-address fields on the customer profile",
        "description": "Use to change the customer's name, company, mailing address, phone, GSTIN, locale, timezone, or notification-email overrides (`email_invoices`, `email_abuse`). Submit only fields you want to change — partial updates supported. Required (must be non-empty if sent): `name`, `country`, `address`, `city`, `state`, `zip`, `phone`. Phone is normalized: parens, dashes, underscores stripped. Timezone must be a valid IANA identifier (e.g. `America/New_York`). Side effects: triggers FraudRecord + MaxMind risk re-scoring on first save, updates Kayako helpdesk username when `name` changes. Returns `{success:true}`. Errors: 401 missing-required field; 422 invalid timezone or empty payload. Sibling ops: `getAccountInfo`, `updateAccountFeatures`, `updateAccountPassword`."
      }
    },
    "/account/2fa": {
      "get": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "2fa_google_key": {
                      "description": "Base64-encoded secret key for TOTP setup.",
                      "type": "string"
                    },
                    "2fa_google_split": {
                      "description": "Human-readable formatted key (chunks of 4 characters).",
                      "type": "string"
                    }
                  }
                }
              }
            },
            "description": "Two-factor authentication setup data."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAccountTfaSetup",
        "summary": "Fetch TOTP secret to enroll a 2FA authenticator app (Google Authenticator etc.)",
        "description": "Use as step 1 of 2FA enrollment. The 160-bit secret is generated on first call and cached in the session until the user completes (or abandons) setup. No body, no path params. Returns `{2fa_google_key, 2fa_google_split}` — render `2fa_google_key` as a QR code (otpauth://totp/My.InterServer:LID?secret=KEY) and display `2fa_google_split` (key chunked into 4-char groups, space-separated) for manual entry. After the user types the 6-digit code from their app, finalize enrollment with `updateAccountTfa`. Calling this multiple times before enrolling reuses the same in-session secret. Errors: 401 if session invalid. Sibling ops: `updateAccountTfa` (verify & enable), `deleteAccountTfa` (disable)."
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "required": [
                  "2fa_google_code"
                ],
                "type": "object",
                "properties": {
                  "2fa_google_code": {
                    "description": "The 6-digit verification code from your authenticator app.",
                    "type": "string"
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "required": [
                  "2fa_google_code"
                ],
                "type": "object",
                "properties": {
                  "2fa_google_code": {
                    "description": "The 6-digit verification code from your authenticator app.",
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/InvalidTwoFactorCode"
          }
        },
        "operationId": "updateAccountTfa",
        "summary": "Verify TOTP code and enable two-factor authentication on the account",
        "description": "Use as step 2 of 2FA enrollment, after `getAccountTfaSetup`. Body: `{2fa_google_code:string}` — the 6-digit code currently displayed by the user's authenticator app for the secret returned from `getAccountTfaSetup`. On verify success, the secret is persisted to `account_security` (type `2fa_google_key`, label `default`) and ALL OTHER active sessions for this account are invalidated (server destroys appsessions and sessions rows where session_id != current). The current session remains. Subsequent logins will require both password and a fresh TOTP code. Returns `{success:true, text}`. Errors: 401 unauthenticated; 422 `Invalid Code` if the TOTP doesn't match (clock skew, wrong app entry, or expired). Sibling ops: `getAccountTfaSetup`, `deleteAccountTfa`."
      },
      "delete": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteAccountTfa",
        "summary": "Disable two-factor authentication and remove the TOTP secret",
        "description": "DESTRUCTIVE: removes the 2FA secret from `account_security` and clears the in-session secret cache. After success, only password authentication is required for future logins — security posture drops materially. No body, no path params. Use when the customer has lost their authenticator device or wants to re-enroll from scratch (call this, then `getAccountTfaSetup` -> `updateAccountTfa`). Returns `{success:true, text:'Google Two Factor Authentication is disabled successfully!'}`. Errors: 401 unauthenticated. Caveat: existing sessions remain valid; rotate `updateAccountPassword` if you suspect credential compromise. Sibling ops: `getAccountTfaSetup`, `updateAccountTfa`, `updateAccountPassword`."
      }
    },
    "/account/apikey": {
      "post": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateAccountApiKey",
        "summary": "Rotate the account's REST/MCP API key — old key is invalidated immediately",
        "description": "DESTRUCTIVE: generates a new 128-character random API key and overwrites the existing entry in `account_security` (type `api_key`, label `default`). The OLD key stops working the moment this returns — any scripts, MCP clients, or CI jobs using the previous key will start receiving 401 until updated. No body, no path params. Returns `{success:true, text:NEW_KEY}` — the plaintext key is returned ONCE in this response and is not retrievable later (only stored hashed-equivalent server-side for verification). Store immediately in a secret manager. Use after suspected credential leak, employee offboarding, or routine rotation. Errors: 401 unauthenticated. Sibling ops: `updateAccountPassword`, `updateAccountIpLimits`, `Logout`."
      }
    },
    "/account/features": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AccountFeatures"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountFeatures"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/NothingToUpdate"
          }
        },
        "operationId": "updateAccountFeatures",
        "summary": "Toggle account-wide safety locks for password reset and OS reinstall",
        "description": "Updates account-level feature flags that gate destructive service operations across every VPS / dedicated / QuickServer the customer owns. Useful for production accounts that want belt-and-suspenders protection against accidental reinstalls or root-password resets via the panel/API. Changes take effect immediately for all subsequent service operations. Sibling ops: `getAccountInfo`, `updateAccountInfo`, `updateAccountIpLimits`.\n\n**Body fields:**\n- `disable_reset` (bool, optional) — when `true`, blocks server / VPS root-password resets account-wide.\n- `disable_reinstall` (bool, optional) — when `true`, blocks OS reinstalls account-wide.\n\nSubmit either or both. Flags absent from the request default to `0` for the comparison and only persist if their value differs from the current stored value.\n\n**Returns:** `{ success: true, text }`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `400` / `422` — `Nothing to update` when neither flag's value differs from current.\n"
      }
    },
    "/account/iplimits": {
      "post": {
        "requestBody": {
          "description": "The lower and upper bounds of an ip range.",
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/IpLimitRange"
              },
              "examples": {
                "IpLimitRangeFormExample": {
                  "value": {
                    "start": "1.2.3.0",
                    "end": "1.2.3.255"
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IpLimitRange"
              },
              "examples": {
                "IpLimitRangeJsonExample": {
                  "value": {
                    "start": "1.2.3.0",
                    "end": "1.2.3.255"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/InvalidIpAddress"
          }
        },
        "operationId": "updateAccountIpLimits",
        "summary": "Add an IP CIDR/range to the account's API+web allow-list (lockout-safe)",
        "description": "DESTRUCTIVE / LOCKOUT-RISK: appends an IP range to `accounts.session_limit`. Once ANY range exists, all `/apiv2` and panel access is restricted to matching source IPs. Body: `{start, end, restrict?}` — both IPv4 dotted-quad; `restrict` is `Web & API` (default) or `Only API`. Safety net: server checks the caller's IP against the resulting list and auto-appends a /32 for the caller if not already covered (response text warns about this). The MCP server sets header `X-API-APP: 1` which short-circuits the IP check entirely (see `api_check_auth_limits()`), so MCP tools keep working. Caveats: `192.168.1.0`-`192.168.1.255` is rejected as a placeholder. Returns `{success:true, text}`. Errors: 400/422 `Invalid IP Address`; 401 unauthenticated. Sibling ops: `deleteIpLimit`, `getAccountInfo`."
      },
      "patch": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IpLimitRange"
              }
            }
          }
        },
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "description": "IP Range removed."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteIpLimit",
        "summary": "Remove one IP range from the account allow-list (PATCH on /account/iplimits)",
        "description": "DESTRUCTIVE: deletes the matching `{start, end}` entry from `accounts.session_limit`. Method is PATCH (not DELETE) because the path collides with `updateAccountIpLimits`. Body: `{start, end}` — must exactly match an existing range (trim-equal on both bounds). Behaviour: if removing this range would leave an empty list, IP limiting is disabled and the account becomes accessible from any IP. If ranges remain but none cover the caller's source IP, the server auto-injects a /32 for the caller to prevent self-lockout (response text warns). MCP callers bypass via `X-API-APP: 1` header. Returns `{success:true, text:'IP Range deleted.'}`. Errors: 400/422 `Invalid IP Address` if `start`/`end` aren't valid IPs; 401 unauthenticated. Sibling ops: `updateAccountIpLimits`, `getAccountInfo`."
      }
    },
    "/account/oauth/{name}": {
      "delete": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteAccountOauthName",
        "summary": "Unlink a third-party OAuth/social provider (Google, GitHub, etc.) from the account",
        "description": "DESTRUCTIVE: removes the linked provider's tokens from `accounts_ext` (rows where `account_key` IN (`{name}_id`,`{name}_url`)). After unlinking, that provider can no longer be used to log in or pre-fill profile data — the user must log in via password (and 2FA if enabled). Path param: `name` (case-insensitive provider key, e.g. `google`, `github`, `facebook`) — must be present in `getOauthConfig().providers`. No request body. Use when the customer wants to revoke a previously authorized social-login. Returns `{success:true, text:'OAuth Provider Unlinked.'}`. Errors: 400 `Invalid Provider Name.` if `name` not configured; 401 unauthenticated. Sibling ops: `logoutAccountOauth`, `getAccountInfo`, `updateAccountPassword`."
      },
      "parameters": [
        {
          "name": "name",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/account/oauth/{name}/logout": {
      "get": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "logoutAccountOauth",
        "summary": "Sign out of the upstream OAuth provider session (does not unlink the account)",
        "description": "Soft de-authorization for a linked OAuth provider — terminates only the upstream provider session/cookie state. The account-level link in `accounts_ext` is preserved, so the user can log back in with that provider without re-linking. Path param: `name` (provider key, e.g. `google`, `github`). No request body. Use when forcing a fresh consent screen on next OAuth login, or after the user reports a stuck/stale provider session. NOT a substitute for `Logout` (which kills the MyAdmin session) and NOT a substitute for `deleteAccountOauthName` (which permanently severs the link). Returns `{success:true, text:'OAuth Provider Logged Out.'}`. Errors: 401 unauthenticated. Sibling ops: `deleteAccountOauthName`, `Logout`, `getAccountInfo`."
      },
      "parameters": [
        {
          "name": "name",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/account/password": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PasswordRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateAccountPassword",
        "summary": "Change the account login password (verifies current, kills other sessions)",
        "description": "DESTRUCTIVE: changes the account login password and invalidates all OTHER active sessions for this account. The current caller's session is preserved; API keys generated via `updateAccountApiKey` remain valid. Sibling ops: `updateAccountApiKey`, `Logout`, `updateAccountTfa`.\n\n**Body fields:**\n- `currentpassword` (string, required) — verified via `auth::authenticate`.\n- `password` (string, required) — must pass `valid_password()` — 8–50 chars, at least one uppercase, one lowercase, one digit, and one of `_~-!@#$%^&*`.\n- `password2` (string, required) — must equal `password`.\n\n**Returns:** `{ success: bool }` — flash messages on the response capture per-field errors.\n\n**Side effects:**\n- Persists `md5(password)` to `accounts.account_passwd`.\n- Sends `password_change_notify.tpl` email to the account login id.\n- Destroys all other sessions for this account row-by-row.\n\n**Errors:**\n- `401` — unauthenticated.\n- Flash `Current login password is mismatching` — bad `currentpassword`.\n- Flash `Confirm Password is mismatching` — `password` ≠ `password2`.\n- Flash password-policy violation message.\n"
      }
    },
    "/account/sshkey": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AccountSshKey"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountSshKey"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateAccountSshKey",
        "summary": "Set the account-level SSH public key auto-installed on new VPS/dedicated orders",
        "description": "Stores or replaces the SSH public key on `account_security` (type `ssh_key`, label `default`). On future VPS, dedicated server, or quickserver orders the activation flow can install this key into `~/.ssh/authorized_keys` for the root/sudo user, eliminating password-based SSH for the initial provisioning. Body: `{sshKey:string}` — full single-line OpenSSH public key (ssh-rsa/ssh-ed25519/ecdsa-sha2-* + base64 + optional comment). Newlines are stripped on save. Existing servers are NOT retroactively updated — only new orders pick this up. Use to set up key-based access ahead of order activation, or to rotate the canonical key. Returns `{success:true, text:'SSH Keys Updated.'}`. Errors: 401 unauthenticated. Sibling ops: `getAccountInfo`, `updateAccountPassword`, `updateAccountApiKey`."
      }
    },
    "/affiliate/banners": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AffiliateBannerRow"
                  }
                },
                "examples": {
                  "AffiliateBannerExample": {
                    "value": [
                      {
                        "image": "12946798.gif",
                        "width": "125",
                        "height": "125"
                      },
                      {
                        "image": "12946800.gif",
                        "width": "160",
                        "height": "90"
                      },
                      {
                        "image": "12946802.gif",
                        "width": "160",
                        "height": "190"
                      },
                      {
                        "image": "12946806.gif",
                        "width": "200",
                        "height": "200"
                      },
                      {
                        "image": "12946808.gif",
                        "width": "250",
                        "height": "250"
                      },
                      {
                        "image": "12946811.gif",
                        "width": "300",
                        "height": "250"
                      }
                    ]
                  }
                }
              }
            },
            "description": "Affiliate Banners Array"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAffiliateBanners",
        "summary": "List affiliate banner image assets with filename and dimensions",
        "description": "Returns the catalog of pre-built banner images affiliates can embed on partner sites — same catalog for every account (not per-affiliate). Use to render a creative-asset picker in the affiliate dashboard. Each row carries the image filename and dimensions so the client can build correctly-sized `<img>` tags. Read-only. Sibling ops: `getAffiliateRichReport`, `getAffiliateSalesGraph`, `getAffiliateTrafficGraph`, `getAffiliateWebTraffic`, `getAffiliateSignups`, `updateAffiliateDockSetup`.\n\n**Path/Query/Body:** None.\n\n**Returns:** Array of `AffiliateBannerRow`:\n- `image` (string) — filename (e.g. `12946798.gif`); served from the affiliate asset bucket.\n- `width` (string) — pixels.\n- `height` (string) — pixels.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n"
      }
    },
    "/affiliate/dock_setup": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AffiliateDockSetup"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AffiliateDockSetup"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateAffiliateDockSetup",
        "summary": "Configure the affiliate landing dock title, description, and referrer coupon",
        "description": "Customizes the branded landing-dock page shown to visitors arriving via the affiliate's referral link, and reserves a unique referrer coupon code that's automatically created across all affiliate-eligible modules. Title/description allow a limited HTML allowlist (`<b>`, `<br>`, `<strong>`, `<hr>`); everything else is entity-escaped. Coupon changes propagate to **all** affiliate modules atomically. Sibling ops: `updateAffiliatePaymentSetup`, `getAffiliateSignups`.\n\n**Body fields (multipart or JSON, schema `AffiliateDockSetup`):**\n- `affiliate_dock_title` (string, optional) — landing-page title. HTML allowlist: `<b>`, `<br>`, `<strong>`, `<hr>`.\n- `affiliate_dock_description` (string, optional) — landing-page body. Same allowlist.\n- `referrer_coupon` (string, optional) — coupon code reservation. Requirements:\n  - ≥ 6 chars.\n  - `^[a-zA-Z0-9]+$` (alphanumeric only).\n  - Must NOT contain `facebook`, `test`, or `interserver` (substring check, case-insensitive).\n  - Must NOT exactly match a reserved word.\n  - Must NOT already exist as a coupon in any affiliate module (`webhosting`, `vps`, `quickservers`, `servers`, `backups`).\n\n**Returns:** `{text: \"<status message>\"}`.\n\n**Side effects:**\n- First time setting `referrer_coupon`: inserts a `coupons` row in each affiliate module (`type=3`, `amount=0.01`, `onetime=1`, `customer=-1`, `usable=1`, `applies=-1`).\n- Changing `referrer_coupon`: renames the coupon across all affiliate modules in one transaction.\n- Updates the account's `affiliate_dock_title`, `affiliate_dock_description`, `referrer_coupon` fields.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `422 The name must be at least 6 characters long`.\n- `422 Invalid Characters, use only standard english letters and numbers`.\n- `422 That is a reserved word that cannot be used here`.\n- `422 <position> is a reserved word that cannot be used here` (substring match against `facebook`/`test`/`interserver`).\n- `409 That name is already taken` — coupon exists in another account's module.\n- `401` — unauthenticated.\n"
      }
    },
    "/affiliate/payment_setup": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/AffiliatePaymentSetup"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AffiliatePaymentSetup"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateAffiliatePaymentSetup",
        "summary": "Configure how affiliate commissions get paid out (PayPal or internal prepay)",
        "description": "Sets the disbursement preferences for affiliate commission payouts. Choose between PayPal payout (provide an email — validated) or internal prepay credit (auto-applied to future invoices via `method=prepay`). Selecting `not set` suspends payouts. Sibling ops: `updateAffiliateDockSetup`, `getAffiliateRichReport`, `getAffiliateDownload`.\n\n**Body fields (multipart or JSON, schema `AffiliatePaymentSetup`):**\n- `affiliate_payment_method` (string, optional) — one of `paypal` / `prepay` / `not set`.\n- `affiliate_paypal` (string, optional, required when method=`paypal`) — email validated by `valid_email()`.\n\n**Returns:** `{text: \"Ok\"}`.\n\n**Side effects:**\n- Updates the account's `affiliate_payment_method` and/or `affiliate_paypal` fields.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `422 Invalid Email` — `affiliate_paypal` fails `valid_email()`.\n- `422 Invalid Payment Method` — value not in `{paypal, prepay, not set}`.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Read current commissions:** `getAffiliateRichReport`, `getAffiliateSalesGraph`.\n- **Export commission report:** `getAffiliateDownload`.\n"
      }
    },
    "/affiliate/rich_report": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAffiliateRichReport",
        "summary": "Read a combined affiliate performance summary (HTML payload)",
        "description": "Returns a server-rendered HTML/text summary report combining commission totals, conversion rates, and traffic in one round-trip — useful for embedding in a dashboard panel. The payload is **not structured JSON** — for chart-friendly data use `getAffiliateSalesGraph` and `getAffiliateTrafficGraph` instead. Backed by `affiliate_summary_report()`. Sibling ops: `getAffiliateSalesGraph`, `getAffiliateTrafficGraph`, `getAffiliateSignups`, `getAffiliateDownload`, `getAffiliateWebTraffic`.\n\n**Path/Query/Body:** None.\n\n**Returns:** `{text: \"<html-or-plain-text-report>\"}`.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Structured time series:** `getAffiliateSalesGraph`, `getAffiliateTrafficGraph`.\n- **Per-signup detail:** `getAffiliateSignups`.\n- **CSV/XLSX export:** `getAffiliateDownload`.\n"
      }
    },
    "/affiliate/sales_graph": {
      "get": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "days",
            "description": "Number of days of sales history to include in the graph data. Determines the time window for the returned data points.",
            "schema": {
              "type": "integer"
            },
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusMonthlyBreakdown"
                }
              }
            },
            "description": "Affiliate sales graph data broken down by time period."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAffiliateSalesGraph",
        "summary": "Read aggregated affiliate sales time-series (monthly buckets) for chart rendering",
        "description": "Returns aggregated sales time-series data — monthly buckets with sale counts/totals — for the requested look-back window. Use to render a sales trend chart in the affiliate dashboard. Bucket granularity is fixed at monthly by `sales_graph_lte_data`; increasing `days` extends the window, it does not change bucket size. Sibling ops: `getAffiliateTrafficGraph` (clicks), `getAffiliateRichReport` (combined summary), `getAffiliateSignups`, `getAffiliateDownload`.\n\n**Query params:**\n- `days` (integer, optional, default `365`) — look-back window in days.\n\n**Returns:** `StatusMonthlyBreakdown` — buckets keyed by month with aggregated sale counts and amounts.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n"
      }
    },
    "/affiliate/traffic_graph": {
      "get": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "days",
            "description": "Number of days of traffic history to include in the graph data. Determines the time window for the returned data points.",
            "schema": {
              "type": "integer"
            },
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MonthlyCounts"
                }
              }
            },
            "description": "Affiliate traffic graph data broken down by time period."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAffiliateTrafficGraph",
        "summary": "Read aggregated affiliate referral click/visit time-series for chart rendering",
        "description": "Returns aggregated click/visit time-series data from the `affiliate_traffic` table — monthly buckets with visit counts — for the requested look-back window. Pair with `getAffiliateSalesGraph` to compute click-to-sale conversion ratios client-side. Sibling ops: `getAffiliateSalesGraph` (sales), `getAffiliateWebTraffic` (raw per-visit log entries), `getAffiliateRichReport`.\n\n**Query params:**\n- `days` (integer, optional, default `180`) — look-back window in days.\n\n**Returns:** `MonthlyCounts` — buckets keyed by month with aggregated visit counts.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n"
      }
    },
    "/affiliate/web_traffic": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AffiliateTrafficRow"
                  }
                },
                "examples": {
                  "AffiliateWebTrafficResponseExample": {
                    "value": [
                      {
                        "traffic_id": "91839913",
                        "traffic_ip": "2a06:98c0:3600::",
                        "traffic_url": "https://www.interserver.net/webhosting/?id=8",
                        "traffic_affiliate": "8",
                        "traffic_referrer": "",
                        "traffic_timestamp": "2023-09-30 06:30:27"
                      },
                      {
                        "traffic_id": "91831932",
                        "traffic_ip": "2a06:98c0:3600::",
                        "traffic_url": "https://www.interserver.net/webhosting/?id=8",
                        "traffic_affiliate": "8",
                        "traffic_referrer": "",
                        "traffic_timestamp": "2023-09-30 03:15:13"
                      }
                    ]
                  }
                }
              }
            },
            "description": "The recent affiliate web traffic"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAffiliateWebTraffic",
        "summary": "List the 20 most recent affiliate referral visits with IP, referrer, timestamp",
        "description": "Returns the 20 most recent raw referral visits from the `affiliate_traffic` table — visitor IP, full referral URL, and timestamp per row. Use to audit traffic sources, identify top referrers, or investigate suspicious click patterns. Hard-coded limit 20 (no pagination); for longer-term analysis use `getAffiliateTrafficGraph` or export via `getAffiliateDownload`. Sibling ops: `getAffiliateTrafficGraph`, `getAffiliateSignups`, `getAffiliateRichReport`, `getAffiliateDownload`.\n\n**Path/Query/Body:** None.\n\n**Returns:** Array of `AffiliateTrafficRow`:\n- `traffic_id` (string) — row id (most-recent-first).\n- `traffic_ip` (string) — visitor IP (IPv4 or IPv6).\n- `traffic_url` (string) — referral landing URL.\n- `traffic_affiliate` (string) — affiliate (= session `account_id`).\n- `traffic_referrer` (string) — HTTP Referer (may be empty).\n- `traffic_timestamp` (string) — `YYYY-MM-DD HH:MM:SS` in account timezone.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n"
      }
    },
    "/affiliate/signups": {
      "get": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "st",
            "description": "Filter signups by status. Use `default` to show all or pass a specific status value to narrow results.",
            "schema": {
              "type": "string"
            },
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "description": "Affiliate signup statistics and client-side data.",
                      "type": "object"
                    }
                  }
                }
              }
            },
            "description": "Affiliate signup statistics."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAffiliateSignups",
        "summary": "Read affiliate signup stats and per-customer conversion data",
        "description": "Returns referred-customer signup statistics with optional status filtering — counts, conversion data, and per-customer detail produced by `affiliates_clientside()`. The inner `data` shape varies by status filter; pass `default` for the full dataset. Sibling ops: `getAffiliateRichReport`, `getAffiliateSalesGraph`, `getAffiliateTrafficGraph`, `getAffiliateDownload`.\n\n**Query params:**\n- `st` (string, optional, default `default`) — status filter. `default` returns all; other values narrow the results to that status.\n\n**Returns:** `{data: <object>}` — signup counts, conversions, per-customer detail (shape depends on `st`).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n"
      }
    },
    "/affiliate/download": {
      "get": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "st",
            "description": "Filter by status.",
            "schema": {
              "type": "string"
            },
            "in": "query"
          },
          {
            "name": "ex",
            "description": "Export format: csv, xls, xlsx, or pdf. Defaults to csv.",
            "schema": {
              "enum": [
                "csv",
                "xls",
                "xlsx",
                "pdf"
              ],
              "type": "string"
            },
            "in": "query"
          },
          {
            "name": "year",
            "description": "Year to filter the report. Defaults to the current year.",
            "schema": {
              "type": "integer"
            },
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Affiliate report file download. The response Content-Type matches the requested format (text/csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, or application/pdf)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getAffiliateDownload",
        "summary": "Export the affiliate signup report as CSV, XLS, XLSX, or PDF file download",
        "description": "Exports the affiliate signup report as a downloadable file in the requested format. Use for accounting, tax filings, or sharing reports outside the dashboard. **Response is a binary stream, not JSON** — the handler emits the file body with matching `Content-Type` + `Content-Disposition: attachment` headers and `exit()`s the request immediately. Consumers must read the raw response body. Sibling ops: `getAffiliateRichReport`, `getAffiliateSignups`, `getAffiliateSalesGraph`.\n\n**Query params:**\n- `ex` (string, optional, enum `csv`/`xls`/`xlsx`/`pdf`, default `csv`) — export format.\n- `st` (string, optional, default `default`) — status filter (same as `getAffiliateSignups`).\n- `year` (integer, optional, default current year) — report scope.\n\n**Returns:** File download with format-appropriate Content-Type:\n- `csv` → `text/csv`, filename `Interserver_Affiliates.csv`.\n- `xls` / `xlsx` → `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`, filename `Interserver_Affiliates.<ext>`.\n- `pdf` → `application/pdf`, filename `Interserver_Affiliates.pdf`.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n"
      }
    },
    "/backups": {
      "get": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/BackupRow"
                  }
                }
              }
            },
            "links": {
              "GetBackupDetails": {
                "operationId": "getBackupInfo",
                "parameters": {
                  "id": "$response.body#/0/backup_id"
                },
                "description": "Use the backup_id from the list to retrieve full service details including connection info and status."
              }
            },
            "description": "The listing of backup storage services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBackupsList",
        "summary": "List off-site backup storage subscriptions on the authenticated account",
        "description": "Use when enumerating all off-site backup storage services (SFTP-style remote storage subscriptions) on the authenticated customer's account. NOT for VPS/QS/webhosting in-place snapshots — those live under their own tags (`getVpsBackups`, `getQsBackups`, `getWebsitesBackups`). No query params, no body.\nReturns an array of rows; each row carries `backup_id`, `backup_name`, `backup_username`, `backup_status`, `services_name` (plan), and `backup_cost` (recurring price from `repeat_invoices`). Use `backup_id` as the path `{id}` for `getBackupInfo`, `getBackupLogin`, `getBackupInvoices`, `getBackupsWelcomeEmail`, `cancelBackup`. Errors: HTTP 401 if unauthenticated. Empty array when the customer has no backup services.\nSiblings: `getBackupInfo`, `getNewBackup`, `addBackup`."
      }
    },
    "/backups/order": {
      "get": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BackupsOrder"
                }
              }
            },
            "description": "Information needed to generate an order form."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewBackup",
        "summary": "Get backup-storage order form metadata and pricing tiers",
        "description": "Use before placing an off-site backup storage order to fetch the available plans, their service-type IDs, and per-tier pricing needed to render an order form. No params, no body.\nReturns `{ packageCosts, serviceTypes }` — `packageCosts` is a map of `services_id` → recurring cost (from `services` where `services_module='backups'` and `services_buyable=1`); `serviceTypes` is the dispatcher output of `run_event('get_service_types', true, 'backups')` describing each tier. Pass the chosen `services_id` as `serviceType` to `validateBackupOrder` (PUT) for a price preview, then to `addBackup` (POST) to commit. Errors: HTTP 401 if unauthenticated.\nSiblings: `validateBackupOrder`, `addBackup`, `getBackupsList`."
      },
      "put": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BackupOrderPutRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BackupOrderPutRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BackupOrderPutResponse"
                }
              }
            },
            "description": "Validate Backup Order Response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "validateBackupOrder",
        "summary": "Validate a backup-storage order and preview pricing without charging",
        "description": "Use to dry-run a backup order — runs `validate_buy_storage()` to compute final price, apply any coupon, and surface validation errors before the customer commits. No invoice is created and no service is provisioned.\nBody (JSON or multipart): `serviceType` (services_id from `getNewBackup`), optional `coupon`, `period` (months, default 1), `comment`. Returns `{ continue, errors, serviceType, serviceCost, originalCost, repeatServiceCost, hostname, password, coupon, couponCode }`. Use the response to render a confirmation screen, then call `addBackup` (POST same path) to place the order. Errors: HTTP 401 unauthenticated; HTTP 422 surfaced inside `errors[]` (invalid coupon, ineligible plan, duplicate hostname).\nSiblings: `addBackup`, `getNewBackup`."
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BackupOrderPutRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BackupOrderPutRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BackupOrderPostResponse"
                }
              }
            },
            "links": {
              "InitiatePayment": {
                "operationId": "getBillingInvoice",
                "parameters": {
                  "id": "$response.body#/invoices_id"
                },
                "description": "Use the invoice ID from the order response to retrieve invoice details or proceed to payment."
              }
            },
            "description": "Response from the backup order call including invoice IDs for payment."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a managed off-site backup storage service. Returns invoice IDs for /billing/pay/{method}/{invoices}.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addBackup",
        "summary": "Place a new off-site backup storage order and generate the invoice",
        "description": "Step 3 of the backup-storage order flow. Revalidates via `validate_buy_storage()`, then calls `place_buy_storage()` which creates a `backups` service row, a `repeat_invoices` recurring entry, and the first `invoices` row. **Real billable order — call `validateBackupOrder` first.** Service is provisioned only after the invoice is paid. Sibling ops: `getNewBackup` (catalog), `validateBackupOrder` (quote), `getBackupInvoices` (billing history), `initiatePayment` (settle).\n\n**Body fields** (JSON or multipart):\n- `serviceType` (integer, required) — `services_id` from `getNewBackup`.\n- `coupon` (string, optional) — coupon code.\n- `period` (integer, optional, default `1`) — billing months.\n- `comment` (string, optional) — saved on the order row.\n\n**Returns** (on success): `{ continue: true, total_cost, iid, iids, real_iids, serviceId, invoice_description, cj_params }` — feed `real_iids` into `initiatePayment`. On validation failure: `{ continue: false, errors: [...] }` with HTTP 200.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n- `422` inside `errors[]` — coupon/plan/duplicate-hostname validation.\n- Explicit error text when no backend storage server is available for assignment.\n\n**Side effects:** new rows in `backups`, `repeat_invoices`, `invoices`; queued provisioning kicks off only after payment.\n\n**Related calls:**\n- **Prerequisite:** `validateBackupOrder`.\n- **Pay:** `getBillingInvoice` → `initiatePayment`.\n- **Poll status:** `getBackupInfo` (until `backup_status='active'`).\n"
      }
    },
    "/backups/{id}": {
      "get": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Backup"
                }
              }
            },
            "links": {
              "GetBackupInvoices": {
                "operationId": "getBackupInvoices",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Retrieve invoices for this backup service."
              },
              "GetBackupLogin": {
                "operationId": "getBackupLogin",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Obtain a login session URL for the backup storage panel."
              },
              "CancelBackup": {
                "operationId": "cancelBackup",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Cancel this backup service."
              }
            },
            "description": "Full backup service details including serviceInfo, billingDetails, and client_links."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBackupInfo",
        "summary": "Get details of a specific off-site backup storage service",
        "description": "Use to fetch the full management view for one backup-storage subscription. Path param: `id` (backup service ID from `getBackupsList`). No body.\nReturns `serviceInfo` (with `backup_username`, `backup_ip`, `backup_status`, `backup_quota`, `backup_type`, `backup_invoice`), plus `billingDetails`, `extraInfoTables`, `package`, `custCurrency`, and `client_links` (rewritten to surface the link target rather than the raw queue URL). `admin_links`, internal `settings`, and `csrf` are stripped. Errors: HTTP 401 unauthenticated; HTTP 404 if `id` does not belong to the caller (cross-account access blocked by `get_service`).\nSiblings: `getBackupLogin` (open storage panel session), `getBackupInvoices`, `getBackupsWelcomeEmail`, `cancelBackup`, `updateBackupInfo`."
      },
      "post": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateBackupInfo",
        "summary": "Update stored metadata for a backup-storage subscription",
        "description": "Use to update non-billing metadata (e.g. stored credentials, comment, hostname) on an existing off-site backup storage service. Path param: `id` from `getBackupsList`. Body fields are forwarded to the same `View::go()` handler as the GET; consult the order form for accepted keys.\nReturns the standard `SuccessTextResponse`. Caveats: this endpoint does NOT change the plan, quota, or billing — those require cancel + reorder via `cancelBackup` and `addBackup`. It also does NOT trigger any backend SFTP credential rotation. Errors: HTTP 401 unauthenticated; HTTP 404 if `id` is not owned by the caller; HTTP 422 on invalid input.\nSiblings: `getBackupInfo`, `cancelBackup`, `getBackupLogin`."
      },
      "delete": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/BackupsCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "cancelBackup",
        "summary": "Cancel an off-site backup storage subscription",
        "description": "DESTRUCTIVE. Use to terminate a backup-storage subscription. Delegates to `CancelService::go($id)` with module `backups`, which marks the service for cancellation and stops future recurring billing; data on the storage backend may become inaccessible at end of cycle. Path param: `id` from `getBackupsList`. No body.\nReturns `BackupsCancelResponse`. Caveats: irreversible — a new order via `addBackup` is required to restore service, with a new IP/username and no migration of prior data. Does NOT delete VPS/QS/webhosting in-place snapshots (those live under their own tags). Errors: HTTP 401 unauthenticated; HTTP 404 if `id` is not owned by the caller; HTTP 409 if the service is already cancelled or pending cancellation.\nSiblings: `addBackup`, `getBackupInfo`, `getBackupInvoices`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The backup service ID. Use the `backup_id` from `GET /backups` to identify the service.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/backups/{id}/invoices": {
      "get": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBackupInvoices",
        "summary": "List invoices for a single backup-storage subscription",
        "description": "Use to retrieve all invoices tied to one off-site backup storage service — useful for confirming billing status, locating an unpaid invoice to pay, or reconciling renewals. Path param: `id` from `getBackupsList`. Delegates to the shared `InvoicesList::go()` handler with module `backups`. No body.\nReturns `ChargeInvoiceRows` (array of invoice rows with `invoices_id`, status, amount, dates). Feed `invoices_id` into `getBillingInvoice` for full detail or `/billing/pay/{method}/{invoices}` to settle an unpaid invoice. For the account-wide invoice list use the Billing tag instead. Errors: HTTP 401 unauthenticated; HTTP 404 if `id` is not owned by the caller.\nSiblings: `getBackupInfo`, `addBackup`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The backup service ID. Use the `backup_id` from `GET /backups` to identify the service.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/backups/{id}/login": {
      "get": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BackupLoginResponse"
                }
              }
            },
            "description": "Login session details for the backup storage service."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBackupLogin",
        "summary": "Open a single sign-on session URL for the backup storage panel",
        "description": "Use to drop the customer straight into the off-site backup storage management panel without a separate login prompt. Calls `get_storage_session($id)` to mint a one-shot session URL; treat the URL as short-lived and credentials-equivalent — do not log or share.\nPath param: `id` from `getBackupsList`. No body. Returns `BackupLoginResponse` (`success`, session URL/token, optional connection hints). On `success=false` the handler returns `json_error(text)` (HTTP 400) with the upstream reason. Errors: HTTP 401 unauthenticated; HTTP 404 if `id` is not owned by the caller; backend errors when the storage server is unreachable.\nSiblings: `getBackupInfo` (SFTP `backup_username`/`backup_ip` for direct connections), `getBackupsWelcomeEmail` (resend setup credentials)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The backup service ID. Use the `backup_id` from `GET /backups` to identify the service.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/backups/{id}/welcome_email": {
      "get": {
        "tags": [
          "Backups"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBackupsWelcomeEmail",
        "summary": "Resend the welcome email for an off-site backup storage service",
        "description": "Use when the original welcome email was lost or never arrived. Resends connection credentials (SFTP host, username, quota) and setup instructions to the account email by invoking the module's `backup_welcome_email($id)` helper.\nPath param: `id` from `getBackupsList`. No body. Returns `SuccessTextResponse` with `text='Welcome Email has been resent.'`. Caveats: only works while the service is `active`; cancelled/pending services will return 409. Email is sent to the customer-of-record on file — there is no override recipient parameter. Errors: HTTP 401 unauthenticated; HTTP 404 if `id` is not owned by the caller (`Invalid Service Passed`); HTTP 409 if `backup_status` is not `active` (`Service is not active`).\nSiblings: `getBackupLogin`, `getBackupInfo`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The backup service ID. Use the `backup_id` from `GET /backups` to identify the service.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/billing/cart": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "description": "Cart contents and checkout metadata.",
                  "type": "object"
                }
              }
            },
            "description": "Current shopping cart contents and available payment methods."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "View shopping cart contents and pick a payment method before checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "getBillingCart",
        "summary": "Read the current shopping cart contents, totals, and available payment methods",
        "description": "Returns the customer's checkout state — every pending/unpaid invoice on the account aggregated as a cart, plus available payment methods, currency totals, and checkout metadata. Use to render a checkout page or, in agent flows, as a pre-payment confirmation step before calling `initiatePayment`. Backed by the `cart` helper module; `modules_json` and `csrf_token` are stripped from the response. Read-only. Sibling ops: `getBillingInvoices` (raw list), `getBillingInvoice` (one invoice in detail), `initiatePayment` (pay), `getBillingPrePays` (check prepay balance first).\n\n**Path/Query/Body:** None.\n\n**Returns:** A cart object with:\n- Line items aggregated from unpaid `invoices` rows for the session account.\n- Currency-normalized subtotal / total.\n- Available payment methods (filtered by feature flags, account country, and which gateways are enabled): `cc`, `paypal`, `btcpay`, `coinbase`, `payu`, `ccavenue`, `cashfree`, `payssion`, `prepay`.\n- Per-invoice description, module, service-id, amount.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **List unpaid invoices directly:** `getBillingInvoices`.\n- **Drill into one invoice:** `getBillingInvoice`.\n- **Pay:** `initiatePayment` (use the cart's invoice ids or the `SERVICEvpsN` / `INVvpsN` tag forms).\n- **Top up prepay first:** `getBillingPrePays`, `addBillingPrepay`.\n"
      }
    },
    "/billing/prepays": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "description": "Prepay balance data.",
                  "type": "object"
                }
              }
            },
            "description": "Prepay balances and metadata."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBillingPrePays",
        "summary": "List prepay deposits on the account — remaining balance and auto-use flags",
        "description": "Returns every prepay deposit on the account — funded or pending — with remaining balances, modules they're scoped to, and the `automatic_use` flag controlling whether the balance auto-applies to future invoices. Use to gate `method=prepay` at checkout (a prepay must be funded to count toward payment) or to render a prepays management page. Read-only. `csrf_token` is stripped from the helper output. Sibling ops: `addBillingPrepay` (top up), `deleteBillingPrepay` (remove), `initiatePayment` (`method=prepay`), `getBillingCart`.\n\n**Path/Query/Body:** None.\n\n**Returns:** Object with per-prepay rows:\n- `prepay_id` (integer).\n- `prepay_module` (string) — service module the prepay is scoped to (or `default` for any).\n- `prepay_amount` (decimal) — original deposit amount.\n- `prepay_remaining` (decimal) — funds left.\n- `prepay_automatic_use` (bool) — auto-apply to invoices.\n- `prepay_paid` (bool) — whether the funding invoice has been paid (unpaid prepays are listed but unusable).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Top up:** `addBillingPrepay` (returns an invoice id you then pay via `initiatePayment`).\n- **Pay with prepay:** `initiatePayment` with `method=prepay`.\n- **Remove an unfunded prepay:** `deleteBillingPrepay`.\n- **Cart view:** `getBillingCart` (includes prepay summary).\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingPrepayRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BillingPrepayRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Top up prepay balance — creates an invoice you then pay via /billing/pay/{method}/{invoices}.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addBillingPrepay",
        "summary": "Create a prepay deposit and return an invoice id to fund it",
        "description": "Creates a prepay row (`prepays` table) at the requested amount and inserts a matching `invoices` row (`Prepay ID {pid} Invoice`) that the customer must pay through `initiatePayment` before the balance becomes usable. The prepay is added with `PREPAY_TYPE_ANY` / `PREPAY_SERVICE_ANY` defaults via `add_prepay()`. Use to seed an account balance the customer can later spend via `method=prepay` at checkout. **Real money** — funding the returned invoice charges a real payment method. Sibling ops: `getBillingPrePays`, `deleteBillingPrepay`, `getBillingInvoice`, `initiatePayment`.\n\n**Body fields (JSON or multipart, schema `BillingPrepayRequest`):**\n- `amount` (number, required) — deposit size in account currency. **Minimum $10**; smaller values are rejected.\n- `module` (string, required) — service module scope (`default` for any service, or specific like `vps`, `webhosting`).\n- `automatic_use` (bool, required) — when `true`, the balance auto-applies to future invoices in the scoped module.\n\n**Returns:** `{text: \"Thank you! Prepay created! Kindly pay the invoice to activate the prepay fund.\", invoice: <integer>}` — pass `invoice` to `initiatePayment` (use a real `method` like `cc` / `paypal`, not `prepay` — you can't fund a prepay with a prepay).\n\n**Side effects:**\n- Inserts `prepays` row.\n- Inserts `invoices` row (`invoices_description = \"Prepay ID {pid} Invoice\"`, `invoices_paid=0`, `invoices_module='default'`).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `Sorry! Minimum prepay amount is $10.00` — amount below floor.\n- `Something went wrong! Try again or contact our support team!` — invoice insert failed.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Confirm invoice:** `getBillingInvoice` with the returned `invoice` id.\n- **Pay it:** `initiatePayment` (`method=cc|paypal|...`, not `prepay`).\n- **Verify it's now usable:** `getBillingPrePays` (look for `prepay_remaining > 0`).\n- **Cancel before paying:** `deleteBillingPrepay`.\n\n**Example happy path:**\n```text\nPOST /apiv2/billing/prepays { \"amount\": 100, \"module\": \"default\", \"automatic_use\": true }\n-> { \"text\": \"...\", \"invoice\": 25296701 }\nGET /apiv2/billing/pay/cc/25296701\n-> { \"type\": \"single\", \"text\": \"Payment processed.\" }\nGET /apiv2/billing/prepays\n-> [{ \"prepay_id\": 99, \"prepay_remaining\": 100, ... }]\n```\n"
      }
    },
    "/billing/prepays/{id}": {
      "delete": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteBillingPrepay",
        "summary": "Delete an unfunded prepay or strip its unpaid funding invoices",
        "description": "Removes a prepay from the account, with one safety rule: a prepay that still has usable credit (`prepay_remaining > $0.01`) cannot be deleted *unless* it also has unpaid funding invoices we can clean up — in which case those unpaid `invoices` rows are deleted and the prepay row stays. Use to back out a never-funded prepay, or to surface stuck unpaid funding invoices. **Irreversible** — funded credit is unrecoverable through this endpoint. Sibling ops: `getBillingPrePays`, `addBillingPrepay`, `deleteBillingInvoice`.\n\n**Path param:**\n- `id` (integer, required) — prepay id from `getBillingPrePays.prepay_id`.\n\n**Body:** None.\n\n**Returns:**\n- When unpaid funding invoices were stripped but prepay still has funds: `\"PrePay {id} Unpaid Invoices Deleted\"`.\n- When the prepay row was deleted: `\"PrePay {id} deleted.\"`.\n\n**Side effects:**\n- Deletes any unpaid `invoices` rows matching `invoices_description = \"Prepay ID {id} Invoice\"` and `invoices_paid=0`.\n- Deletes the `prepays` row when remaining balance ≤ $0.01.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `Invalid Prepay` — `id` not found.\n- `That prepay still hands funds available on it` — funds remain AND no unpaid invoices to clean up.\n- `There was an error deleting the prepay, please contact support` — delete affected 0 rows.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **List first:** `getBillingPrePays`.\n- **Re-add later:** `addBillingPrepay`.\n- **Cancel a specific funding invoice:** `deleteBillingInvoice` (routes prepay invoices here automatically).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The prepay balance ID to delete.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/billing/invoices": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingInvoiceList"
                }
              }
            },
            "links": {
              "GetInvoiceDetails": {
                "operationId": "getBillingInvoice",
                "parameters": {
                  "id": "$response.body#/invoices/0/id"
                },
                "description": "Use an invoice ID from the list to retrieve the full invoice details for payment or review."
              }
            },
            "description": "Invoice listing and summary for the account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBillingInvoices",
        "summary": "List every invoice on the account with summary totals and paid/unpaid status",
        "description": "Returns the customer's complete invoice ledger — every charge, paid or unpaid, across every service module. Use to render a billing-history page, find an unpaid invoice id to pass to `initiatePayment`, or audit recent activity. Server-side strips the first synthetic header row from `get_view_invoices()` and reindexes the array. Read-only. The response includes a Link to `getBillingInvoice` for drilling into any row. Sibling ops: `getBillingInvoice`, `deleteBillingInvoice`, `initiatePayment`, `getBillingCart`, `getBillingPrePays`.\n\n**Path/Query/Body:** None.\n\n**Returns:** `BillingInvoiceList` — object containing:\n- `rows` (array) — per-invoice summaries: `id`, `amount`, `paid`, `description`, `date`, `due_date`, `module`, `service` (service-id within the module), `currency`.\n- Aggregate totals across the array (totals object: `total`, `paid_total`, `unpaid_total`).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Drill into one invoice:** `getBillingInvoice`.\n- **Pay an unpaid invoice:** `initiatePayment`.\n- **Cancel an unpaid pending-service invoice:** `deleteBillingInvoice` (only works on pending services / unpaid prepays).\n- **Per-service invoices instead:** `getVpsInvoices`, `getDomainInvoices`, `getMailInvoices`, `getBackupInvoices`, etc.\n"
      }
    },
    "/billing/invoices/{id}": {
      "get": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The invoice ID. Use IDs from `GET /billing/invoices` or from order responses.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingInvoiceDetail"
                }
              }
            },
            "links": {
              "DeleteInvoice": {
                "operationId": "deleteBillingInvoice",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Delete this invoice if it is still unpaid."
              }
            },
            "description": "Detailed invoice payload for the requested invoice."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBillingInvoice",
        "summary": "Read full invoice detail — line items, totals, paid status, customer info",
        "description": "Returns the full rendered invoice payload for a single invoice — backed by `get_invoice_data()`, the same helper that builds the email-style invoice document. Use to confirm the exact balance due and the invoice description before calling `initiatePayment`, or to render an invoice viewer page. Read-only. The response is an email-style/HTML payload (not a structured line-item array) — for a structured cart-style summary use `getBillingCart`. The response includes a Link to `deleteBillingInvoice` for unpaid pending-service invoices. Sibling ops: `getBillingInvoices`, `deleteBillingInvoice`, `initiatePayment`, `getBillingCart`, per-service `getVpsInvoices` / `getMailInvoices` / etc.\n\n**Path param:**\n- `id` (integer, required) — invoice id from `getBillingInvoices.rows[].id`, from an order endpoint's response (e.g. `addVps.iid`), or from a per-service invoice list.\n\n**Body:** None.\n\n**Returns:** `BillingInvoiceDetail` — full rendered invoice payload (email body) with line items, totals, customer/billing info, and paid status. The exact shape mirrors what gets sent to the customer.\n\n**Auth:** Session/API key. Ownership enforced through the invoice's `invoices_custid`.\n\n**Errors:**\n- `Invalid Invoice` — `id` not found or owned by another account.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Pay it:** `initiatePayment` (`/billing/pay/{method}/{id}`).\n- **Delete if pending/unpaid:** `deleteBillingInvoice`.\n- **List all:** `getBillingInvoices`.\n- **Cart-style summary across all unpaid:** `getBillingCart`.\n"
      },
      "delete": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The invoice ID to delete. Only unpaid invoices can be deleted.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteBillingInvoice",
        "summary": "Cancel a pending unpaid invoice — and its pending service or repeat invoice",
        "description": "Cancels an unpaid invoice and cleans up the records it represents. Behavior depends on what the invoice funds: a **prepay** invoice is routed to `deleteBillingPrepay`; an **initial service charge** (where `repeat_invoices_id` matches the service's `_invoice` field) deletes the `repeat_invoices` row, all child `invoices`, AND the pending service row from the module's table; an **addon/recurring** invoice just deletes that one `invoices` row plus its `repeat_invoices` row. **Only invoices for services in `pending` status can be deleted** — once provisioned, the service must be cancelled via the per-service Cancel endpoint instead. **Irreversible**. Sibling ops: `getBillingInvoice`, `deleteBillingPrepay`, `VPSCancel` / `CancelDomain` / `mailCancel` / `webhostingCancel` / etc.\n\n**Path param:**\n- `id` (integer, required) — invoice id (`invoices_type=1`, ownership enforced via `invoices_custid`).\n\n**Body:** None.\n\n**Returns:** `Invoice Deleted` text.\n\n**Side effects:** (depends on invoice type)\n- **Prepay invoice** (description matches `Prepay ID N Invoice`) — delegates to `deleteBillingPrepay($pid)`.\n- **Initial service invoice** (`repeat_invoices_id == service._invoice`) — deletes:\n  - the `repeat_invoices` row,\n  - every `invoices` row for that service,\n  - the service row in `{settings['TABLE']}`.\n- **Addon/recurring invoice** — deletes only the matching `repeat_invoices` row and the single `invoices` row.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `Invalid invoice` — `id` not found or wrong owner.\n- `Invalid service` — invoice references a service that no longer exists.\n- `Can only delete invoices for pending services or prepays` — service is `active`/`suspended`/`cancelled`.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **List candidates:** `getBillingInvoices`.\n- **Detail first:** `getBillingInvoice`.\n- **For active services:** `VPSCancel`, `CancelDomain`, `mailCancel`, `webhostingCancel`, `licensesCancel`, `sslCancel`, `cancelScrubIp`, `floating_ipsCancel`, `cancelBackup`, `quickserversCancel`, `serversCancel` — these use `Billing\\CancelService::go()`.\n- **For prepay invoices:** `deleteBillingPrepay` (delegated automatically).\n"
      }
    },
    "/billing/creditcards": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingAddCcRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BillingAddCcRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "addBillingCreditCard",
        "summary": "Store a credit card on the account — may return a verification flow",
        "description": "Stores a new credit card on the account so it can later be selected via `updateBillingPaymentMethod` or used directly with `initiatePayment` (`method=cc`). The card number has dashes stripped and is sanitized through `FILTER_SANITIZE_NUMBER_INT`; billing address fields are HTML-entity-escaped server-side; the CC number is encrypted at rest via `App::encrypt()`. The flow may return `action='verify'` indicating a two-step micro-charge verification is required before the card is usable — complete it with `patchBillingCreditCardVerify` then `postBillingCreditCardVerify`. Sibling ops: `updateBillingCreditCard`, `deleteBillingCreditCard`, `patchBillingCreditCardVerify`, `postBillingCreditCardVerify`, `updateBillingPaymentMethod`.\n\n**Body fields (JSON or multipart, schema `BillingAddCcRequest`):**\n- `cc` (string, required) — card number; dashes stripped, non-digits filtered.\n- `name` (string, required) — cardholder name.\n- `cc_exp` (string, required) — `MM/YYYY`.\n- `address` (string, required), `city`, `state`, `country`, `zip` (strings) — billing address; HTML-entity-escaped.\n\n**Returns:**\n- **Added directly:** `{success: true, text: \"Card Added Successfully!\"}`.\n- **Verification needed:** `{success: false, text: \"Kindly verify your card by updating the amounts in the fields\", action: \"verify\"}` — proceed to `patchBillingCreditCardVerify`.\n\n**Side effects:**\n- Inserts the encrypted card into the account's `ccs` array (managed via `parse_ccs` / `add_cc`).\n- May trigger a small initial test charge (gateway-dependent).\n- First-card-on-account triggers MaxMind + FraudRecord risk-score recomputation.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `Card number, Full Name, Expiry date are required!` — required field missing/empty.\n- `401` — unauthenticated.\n- Gateway/AVS error text — declined, mismatch, etc.\n\n**Related calls:**\n- **Verify (if `action='verify'`):** `patchBillingCreditCardVerify` (CVV + initiate micro-charge) → `postBillingCreditCardVerify` (submit amounts).\n- **Make it the default:** `updateBillingPaymentMethod` with `payment_method=cc<idx>`.\n- **Pay an invoice with it:** `initiatePayment` (`method=cc`).\n"
      }
    },
    "/billing/creditcards/{id}": {
      "post": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The credit card ID. Use IDs from `GET /billing/creditcards` or the response from `POST /billing/creditcards`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateBillingCreditCard",
        "summary": "Refresh stored card expiration and re-trigger MaxMind fraud scoring",
        "description": "Updates the expiration date on a stored credit card and re-encrypts the card record. If the updated card matches the account's primary `cc`, the account-level `cc_exp` is also refreshed. If no MaxMind risk score exists yet for the card, `update_maxmind()` is called to compute one. Use to fix an upcoming expiration before recurring charges fail. Sibling ops: `addBillingCreditCard`, `deleteBillingCreditCard`, `getBillingCreditCardVerify`, `postBillingCreditCardVerify`, `updateBillingPaymentMethod`.\n\n**Path param:**\n- `id` (integer, required) — credit card index (the key in the account's `ccs` array, returned by `parse_ccs` and surfaced as `cc<idx>` in `updateBillingPaymentMethod`).\n\n**Body fields:**\n- `cc_exp` (string, required) — new expiration in `MM/YYYY` format.\n\n**Returns:** `Card updated successfully.`.\n\n**Side effects:**\n- Updates the `ccs` array (re-serialized via `myadmin_stringify`) on the account.\n- When the card == primary `cc`, the account-level `cc_exp` is also written.\n- Triggers `update_maxmind($custid, false, $cc_idx)` if no risk score exists.\n\n**Auth:** Session/API key. Card ownership enforced via `parse_ccs`.\n\n**Errors:**\n- `Invalid Credit Card Passed` — `id` not in `parse_ccs`.\n- `Please enter valid card expiry date` — `cc_exp` body field missing.\n- `Invalid expiration date. It must be in the form of MM/YYYY` — wrong format.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Verify a freshly added card:** `patchBillingCreditCardVerify` → `postBillingCreditCardVerify`.\n- **Remove the card:** `deleteBillingCreditCard`.\n- **Make it default:** `updateBillingPaymentMethod` with `payment_method=cc<idx>`.\n"
      },
      "delete": {
        "tags": [
          "Billing"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The credit card ID to remove. Use IDs from `GET /billing/creditcards`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteBillingCreditCard",
        "summary": "Remove a stored credit card from the account",
        "description": "Removes the indexed credit card from the account's `ccs` collection. If the deleted card was also the account's primary `cc`, the primary field is cleared — `initiatePayment` (`method=cc`) will then return an error until a new default is designated via `updateBillingPaymentMethod`. **Irreversible** — to re-store the same card, re-run `addBillingCreditCard`. Sibling ops: `addBillingCreditCard`, `updateBillingCreditCard`, `updateBillingPaymentMethod`, `getBillingCreditCardVerify`.\n\n**Path param:**\n- `id` (integer, required) — credit card index from `parse_ccs`.\n\n**Body:** None.\n\n**Returns:** `Card removed successfully.`.\n\n**Side effects:**\n- Removes the entry from the `ccs` array; re-serialized via `myadmin_stringify`.\n- When the deleted card was primary: clears account-level `cc`.\n\n**Auth:** Session/API key. Card ownership enforced.\n\n**Errors:**\n- `Invalid Credit Card Passed` — `id` not in `parse_ccs`.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Set a new default:** `updateBillingPaymentMethod`.\n- **Add a replacement:** `addBillingCreditCard`.\n"
      }
    },
    "/billing/creditcards/{id}/verify": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getBillingCreditCardVerify",
        "summary": "Probe whether a stored card still needs micro-charge verification",
        "description": "Status probe for the credit-card verification flow. Read-only — current implementation returns a placeholder string indicating verification is pending; the actual two-step verification happens via `patchBillingCreditCardVerify` (initiate dual micro-charge with CVV) followed by `postBillingCreditCardVerify` (submit the charged amounts). Use to drive the UI's \"verify card\" form rendering. Sibling ops: `patchBillingCreditCardVerify`, `postBillingCreditCardVerify`, `addBillingCreditCard`, `updateBillingPaymentMethod`.\n\n**Path param:**\n- `id` (integer, required) — credit card index from `parse_ccs`.\n\n**Body:** None.\n\n**Returns:** `Verification requirements` (placeholder text — reserved for future structured response with `requires_cvv` / `requires_amounts` flags).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Step 1 of verify flow:** `patchBillingCreditCardVerify`.\n- **Step 2 of verify flow:** `postBillingCreditCardVerify`.\n- **Add a new card:** `addBillingCreditCard`.\n"
      },
      "patch": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "cc_ccv2"
                ],
                "properties": {
                  "cc_ccv2": {
                    "description": "The CVV/CVC code on the back of the credit card.",
                    "type": "string"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "cc_ccv2"
                ],
                "properties": {
                  "cc_ccv2": {
                    "description": "The CVV/CVC code on the back of the credit card.",
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "patchBillingCreditCardVerify",
        "summary": "Place two micro-charges on the card to start CVV verification (step 1 of 2)",
        "description": "Step 1 of the two-step card-verification flow. After `addBillingCreditCard` returns `action='verify'`, call this with the card's CVV to place two small charges (cents-scale) on the card. The customer must then look up the exact amounts in their bank statement and submit them via `postBillingCreditCardVerify` to finalize verification. **After 3 failed CVV attempts** (`cc_fails_<cc>` counter on the account) the card is locked from further verification attempts — contact support. Sibling ops: `getBillingCreditCardVerify`, `postBillingCreditCardVerify`, `addBillingCreditCard`, `updateBillingPaymentMethod`.\n\n**Path param:**\n- `id` (integer, required) — credit card index from `parse_ccs`.\n\n**Body fields:**\n- `cc_ccv2` (string, required) — the 3- or 4-digit CVV/CVC code from the back (or front, for Amex) of the card.\n\n**Returns:** `Your card is charged. Please enter the amounts charged up!` — surface to the UI to prompt for the two amounts.\n\n**Side effects:**\n- Places two test charges via `verify_cc_charge()` (gateway-side).\n- On failure: increments `cc_fails_<cc>` on the account.\n\n**Auth:** Session/API key. Card ownership enforced.\n\n**Errors:**\n- `Invalid Credit Card Passed` — `id` not in `parse_ccs`.\n- `Reached the max number of tries to authenticate this card` — `cc_fails_<cc> > 3`.\n- `Missing or blank CVV` — `cc_ccv2` absent or empty.\n- Gateway error text — charge attempt failed.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Prerequisite:** `addBillingCreditCard` (must have returned `action='verify'`).\n- **Next (step 2):** `postBillingCreditCardVerify` (submit `cc_amount1` + `cc_amount2`).\n- **After verification:** `updateBillingPaymentMethod` to make it the default.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingVerifyCcRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BillingVerifyCcRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postBillingCreditCardVerify",
        "summary": "Submit two micro-charge amounts to finalize card verification (step 2 of 2)",
        "description": "Step 2 of the two-step card-verification flow. Submits the two exact micro-charge amounts the customer saw on their statement (placed by `patchBillingCreditCardVerify`) so the gateway can confirm the customer controls the card. On success, the card is marked verified and can be selected via `updateBillingPaymentMethod` (`payment_method=cc<idx>`) or used directly with `initiatePayment` (`method=cc`). After 3 failed attempts (`cc_fails_<cc> > 3`) the card is locked. Sibling ops: `getBillingCreditCardVerify`, `patchBillingCreditCardVerify`, `addBillingCreditCard`, `updateBillingPaymentMethod`.\n\n**Path param:**\n- `id` (integer, required) — credit card index from `parse_ccs`.\n\n**Body fields (schema `BillingVerifyCcRequest`):**\n- `cc_amount1` (number, required) — first micro-charge amount (in dollars, decimal).\n- `cc_amount2` (number, required) — second micro-charge amount.\n\n**Returns:** Verification success text (gateway-returned).\n\n**Side effects:**\n- Marks the card as verified when amounts match.\n- On failure: increments `cc_fails_<cc>` on the account.\n\n**Auth:** Session/API key. Card ownership enforced.\n\n**Errors:**\n- `Invalid Credit Card Passed` — `id` not in `parse_ccs`.\n- `Reached the max number of tries to authenticate this card` — `cc_fails_<cc> > 3`.\n- `Missing charge amounts` — `cc_amount1` or `cc_amount2` absent.\n- Verification failure text (status `failed` / `error` / `warning`) — amounts don't match.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Prerequisite (step 1):** `patchBillingCreditCardVerify`.\n- **Next:** `updateBillingPaymentMethod` to make the verified card default, or `initiatePayment` (`method=cc`) to pay immediately.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The credit card ID to verify. Use the ID returned from `POST /billing/creditcards`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/billing/payment_method": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingPaymentMethodRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/BillingPaymentMethodRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateBillingPaymentMethod",
        "summary": "Set the account's default payment method for recurring/auto charges",
        "description": "Sets the account's preferred payment method for recurring/automatic charges and (when applicable) promotes a specific stored credit card to be the primary `cc` on the account. Use after `addBillingCreditCard` + verification to select the new card, or when switching between PayPal and credit-card billing. First-time payment-method assignment triggers `update_maxmind()` and `update_fraudrecord()` risk-score generation. Sibling ops: `addBillingCreditCard`, `postBillingCreditCardVerify`, `deleteBillingCreditCard`, `initiatePayment`.\n\n**Body fields (JSON or multipart, schema `BillingPaymentMethodRequest`):**\n- `payment_method` (string, required) — one of:\n  - `cc` — use the existing primary credit card.\n  - `cc<idx>` (e.g. `cc2`) — promote the card at index `idx` (from `parse_ccs`) to primary. Must be verified.\n  - `paypal` — switch to PayPal.\n- `cc_auto` (string `0`/`1`, optional) — auto-charge flag. Implicitly set to `1` when selecting `cc`/`cc<idx>`, `0` for `paypal`.\n\n**Returns:** `{text: \"Payment Method Updated\"}`.\n\n**Side effects:**\n- When `payment_method=cc<idx>`: copies the indexed card's encrypted `cc` and `cc_exp` onto the account's primary fields.\n- First time a payment method is set: runs MaxMind risk score, then FraudRecord score.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `Invalid Credit Card Specified` — `cc<idx>` is malformed or `idx` not found in `parse_ccs`.\n- `This CC has not been verified.` — the chosen card hasn't completed `postBillingCreditCardVerify`.\n- `Invalid Payment Method Specified` — value not in `{cc, paypal, cc<idx>}`.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Prerequisite for `cc<idx>`:** `addBillingCreditCard` → `patchBillingCreditCardVerify` → `postBillingCreditCardVerify`.\n- **Now pay an invoice:** `initiatePayment` (`method=cc` will use the default; `method=paypal` if you switched).\n- **Audit current methods:** `getAccountInfo` (account profile shows cards as masked).\n"
      }
    },
    "/captcha": {
      "get": {
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaptchaResponse"
                },
                "examples": {
                  "CaptchaResponseExample": {
                    "value": {
                      "captcha": "data:image/jpeg;base64,/9j/4AAQ"
                    }
                  }
                }
              }
            },
            "description": "An array containing a `captcha` field containing a string with a base64 encoded captcha image."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "getCaptcha",
        "summary": "Fetch a base64 JPEG captcha challenge for human verification",
        "description": "Fetches a fresh captcha challenge image to display before submitting `submitSignup` (or any unauthenticated form that needs human verification). Public endpoint — no authentication required. Sibling ops: `getLoginInfo` (returns a captcha alongside other login-page data), `submitSignup` (consumes the answer), `submitLogin`.\n\n**Path/Query/Body:** None.\n\n**Returns:** `{ captcha: string }` — `captcha` is a `data:image/jpeg;base64,...` URL ready to drop into an `<img src>`.\n\n**Side effects:** the phrase is stored server-side in `$_SESSION['captcha']` (also aliased to the signup-flow key `$_SESSION['captchaSignup']` and forgot-password key `$_SESSION['captchaFP']`). The browser must send the same `PHPSESSID` cookie back when posting the answer.\n\n**Charset:** 8 chars from `3456789ABCDEFGHJKLMNPQRSTWXY` — no ambiguous `0`/`1`/`I`/`O`/`2`/`Z`.\n\n**Related calls:**\n- **Consumers:** `submitSignup`, `submitLogin`.\n- **One-shot login bootstrap:** `getLoginInfo`.\nanswer in `captcha` field).\n"
      }
    },
    "/buy_now_servers_list": {
      "get": {
        "tags": [
          "Servers",
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuyItNowList"
                },
                "example": [
                  {
                    "server_id": "10467",
                    "cpu": [
                      "Xeon E3-1270v6",
                      {
                        "img": "xeon-e3.png",
                        "type": "XEON",
                        "speed": "",
                        "num_cpus": "1",
                        "num_cores": "4"
                      }
                    ],
                    "memory": "64GB",
                    "disk": {
                      "4TB SSD": "4TB SSD"
                    },
                    "bandwidth": "1Gbps Unmetered",
                    "ips": "1 Vlan Ip (/30)",
                    "location": "NYC Region",
                    "price": 64
                  }
                ]
              }
            },
            "description": "Marketplace Buy it now servers list"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMPServers",
        "summary": "List Rapid Deploy (Buy-It-Now) marketplace dedicated servers with live pricing",
        "description": "Use to browse pre-built dedicated servers ready for immediate provisioning (Rapid Deploy / marketplace). No params, no body. Pulls live inventory from `mynew.interserver.net/ajax/server_a.php`.\nReturns: array of `{ server_id, cpu: [model, {img,type,speed,num_cpus,num_cores}], memory, disk, bandwidth, ips, location, price }`. The `server_id` is the marketplace asset id — feed it into `buyItNowServerOrder` (GET options for asset `?a=<id>`) and `placeBuyNowServer` (POST to commit). Errors: 401 if session expired.\nSibling ops: `buyItNowServerOrder` (configure asset), `placeBuyNowServer` (purchase), `getNewServer`/`addServer` (custom-spec build, not pre-built), `getServerList` (already-owned servers)."
      }
    },
    "/dns": {
      "summary": "DNS Management",
      "description": "View and manage your DNS domains and records",
      "get": {
        "tags": [
          "DNS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DnsListItem"
                  }
                },
                "examples": {
                  "DnsListExample": {
                    "value": [
                      {
                        "id": 68,
                        "name": "hussfamily.com",
                        "content": "64.20.35.186"
                      },
                      {
                        "id": 1658,
                        "name": "detain.interserver.net",
                        "content": "66.45.228.74"
                      },
                      {
                        "id": 1659,
                        "name": "creation.interserver.net",
                        "content": "66.23.239.99"
                      }
                    ]
                  }
                }
              }
            },
            "links": {
              "GetDnsDomain": {
                "operationId": "getDnsDomain",
                "parameters": {
                  "id": "$response.body#/0/id"
                },
                "description": "Use the domain id from the list to retrieve all DNS records for that domain."
              }
            },
            "description": "Listing of DNS domains on the account with their primary A record."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDnsList",
        "summary": "List DNS zones hosted on the account with each zone's apex A-record IP",
        "description": "Returns every PowerDNS-hosted authoritative zone owned by the authenticated account, one row per zone, with the IP from the apex `A` record. Canonical entry point for discovering zone IDs before reading or editing records. The list is filtered server-side by session `account_id` — cross-account zones are never returned. Empty array means the account holds no zones (not an error). **Note:** this is the hosted DNS zone list, not registrar delegation — use the Domains tag's `updateDomainNameservers` to point a registered domain at `cdns1.interserver.net`/`cdns2.interserver.net`. Sibling ops: `getDnsDomain`, `addDnsDomain`, `addDnsRecord`, `deleteDnsDomain`.\n\n**Path/Query/Body:** None.\n\n**Returns:** Array of `DnsListItem`:\n- `id` (integer) — zone ID; pass to `getDnsDomain` / `addDnsRecord` / `deleteDnsDomain`.\n- `name` (string) — zone FQDN (e.g. `example.com`).\n- `content` (string) — IP from the apex `A` record matching the zone name (empty when no apex A exists yet).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Per-zone record list:** `getDnsDomain`.\n- **Add a zone:** `addDnsDomain`.\n- **Add a record to an existing zone:** `addDnsRecord`.\n- **Registrar delegation:** `getDomainNameservers` / `updateDomainNameservers` (Domains tag).\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DnsNewDomain"
              },
              "examples": {
                "DnsNewDomainExample": {
                  "value": {
                    "domain": "mydomain.com",
                    "ip": "1.2.3.4"
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DnsNewDomain"
              },
              "examples": {
                "DnsNewDomainExampleJson": {
                  "value": {
                    "domain": "mydomain.com",
                    "ip": "1.2.3.4"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "DNS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "addDnsDomain",
        "summary": "Create a new authoritative DNS zone seeded with apex A + NS + SOA records",
        "description": "Creates a new authoritative zone in PowerDNS for this account and seeds it with a default record set: apex `A` record pointing at `ip`, `NS` records for InterServer's `cdns1.interserver.net` / `cdns2.interserver.net` anycast resolvers, and an `SOA`. Served immediately by InterServer's nameservers via supermaster propagation. **Important:** this only creates the hosted zone — the customer must still point their registrar's nameservers at `cdns1.interserver.net` / `cdns2.interserver.net` for queries to resolve through this zone (use `updateDomainNameservers` if the domain is registered through InterServer). Sibling ops: `getDnsList`, `getDnsDomain`, `addDnsRecord`, `updateDomainNameservers`.\n\n**Body fields (form or JSON, schema `DnsNewDomain`):**\n- `domain` (string, required) — FQDN of the zone (e.g. `example.com`).\n- `ip` (string, required) — IPv4 address for the apex A record.\n\n**Returns:** `SuccessTextResponse` — status text confirming the zone was created.\n\n**Side effects:**\n- Inserts `domains` row scoped to session `account_id`.\n- Inserts default `records` rows: apex `A`, two `NS`, one `SOA`.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `400` — missing `domain` or `ip`.\n- `401` — unauthenticated.\n- `409` — zone already exists.\n\n**Related calls:**\n- **Find new zone id:** `getDnsList`.\n- **Add more records:** `addDnsRecord`.\n- **Update registrar nameservers:** `updateDomainNameservers` (Domains tag).\n\n**Example request:**\n```json\n{ \"domain\": \"mydomain.com\", \"ip\": \"203.0.113.42\" }\n```\n"
      }
    },
    "/dns/{id}": {
      "summary": "DNS Domain Management",
      "description": "View and manage a domain and the records for it.",
      "get": {
        "tags": [
          "DNS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The DNS domain ID. Use the `id` from `GET /dns` to identify the domain.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DnsRecord"
                  }
                },
                "examples": {
                  "DnsRecordExample": {
                    "value": [
                      {
                        "id": "473",
                        "domain_id": "68",
                        "name": "hussfamily.com",
                        "type": "NS",
                        "content": "cdns1.interserver.net",
                        "ttl": "86400",
                        "prio": "0",
                        "disabled": "0",
                        "ordername": "",
                        "auth": "1"
                      },
                      {
                        "id": "474",
                        "domain_id": "68",
                        "name": "hussfamily.com",
                        "type": "NS",
                        "content": "cdns2.interserver.net",
                        "ttl": "86400",
                        "prio": "0",
                        "disabled": "0",
                        "ordername": "",
                        "auth": "1"
                      },
                      {
                        "id": "475",
                        "domain_id": "68",
                        "name": "hussfamily.com",
                        "type": "A",
                        "content": "64.20.35.186",
                        "ttl": "86400",
                        "prio": "0",
                        "disabled": "0",
                        "ordername": "",
                        "auth": "1"
                      }
                    ]
                  }
                }
              }
            },
            "links": {
              "UpdateDnsRecord": {
                "operationId": "updateDnsRecord",
                "parameters": {
                  "domainId": "$request.path.id",
                  "recordId": "$response.body#/0/id"
                },
                "description": "Use a record id from the list to update that DNS record."
              },
              "DeleteDnsRecord": {
                "operationId": "deleteDnsRecord",
                "parameters": {
                  "domainId": "$request.path.id",
                  "recordId": "$response.body#/0/id"
                },
                "description": "Use a record id from the list to delete that DNS record."
              }
            },
            "description": "The DNS records for the specified domain."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDnsDomain",
        "summary": "List every DNS record in one zone with the IDs needed to edit or delete them",
        "description": "Returns the full record set for the specified PowerDNS zone (NS, A, AAAA, CNAME, MX, TXT, SRV, CAA, SOA, etc.) in a single response. Ownership is enforced via `get_dns_domain($id)` against the session account — cross-account access returns an error rather than 200. Use a returned record `id` together with the zone `id` to call `updateDnsRecord` or `deleteDnsRecord`. Sibling ops: `getDnsList`, `addDnsRecord`, `updateDnsRecord`, `deleteDnsRecord`, `deleteDnsDomain`.\n\n**Path param:**\n- `id` (integer, required) — zone ID from `getDnsList.id`.\n\n**Returns:** Array of `DnsRecord`:\n- `id` (string) — record ID; pass to `updateDnsRecord` / `deleteDnsRecord`.\n- `domain_id` (string) — parent zone ID.\n- `name` (string) — FQDN of the record (apex or subdomain).\n- `type` (string) — `A` / `AAAA` / `CNAME` / `MX` / `TXT` / `NS` / `SRV` / `CAA` / `SOA` / `PTR` / `SPF` / `TLSA`.\n- `content` (string) — record value (IP for A/AAAA, hostname for CNAME/NS/MX, free text for TXT, etc.).\n- `ttl` (string) — seconds; default 86400.\n- `prio` (string) — priority for MX/SRV (`0` for non-priority records).\n- `disabled` (string `0`/`1`), `ordername` (string), `auth` (string `0`/`1`).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `invalid or missing domain or record id` — zone not found or owned by another account.\n\n**Related calls:**\n- **Add a record:** `addDnsRecord` (POST same path).\n- **Update a record:** `updateDnsRecord` (`POST /dns/{domainId}/{recordId}`).\n- **Delete a record:** `deleteDnsRecord`.\n- **Delete the whole zone:** `deleteDnsDomain` (DELETE same path).\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DnsNewRecord"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DnsNewRecord"
              }
            }
          },
          "required": true
        },
        "tags": [
          "DNS"
        ],
        "responses": {
          "200": {
            "description": "Add DNS Domain Response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "addDnsRecord",
        "summary": "Add a DNS record (A, AAAA, MX, TXT, CNAME, NS, SRV, CAA, ...) to a zone",
        "description": "Adds a single record to the zone identified by path `id`. Type is validated against the global `$rtypes` allowlist (A, AAAA, CNAME, MX, TXT, NS, SRV, CAA, PTR, SPF, TLSA, etc.); content is validated against the record type by `validate_input()`. The record goes live on PowerDNS immediately; resolvers honor the existing TTL on any cached answer. Sibling ops: `getDnsDomain` (find record id afterward), `updateDnsRecord`, `deleteDnsRecord`.\n\n**Path param:**\n- `id` (integer, required) — zone ID from `getDnsList.id`.\n\n**Body fields (form or JSON, schema `DnsNewRecord`):**\n- `name` (string, required) — FQDN of the record (must be at or below the zone apex).\n- `type` (string, required) — `A` / `AAAA` / `CNAME` / `MX` / `TXT` / `NS` / `SRV` / `CAA` / `PTR` / `SPF` / `TLSA` (must be in `$rtypes`).\n- `content` (string, required) — value matching `type` syntax (IPv4 for A, IPv6 for AAAA, hostname for CNAME/NS/MX, free text for TXT).\n- `ttl` (integer, optional, default 86400) — seconds.\n- `prio` (integer, optional, default 0) — priority (MX/SRV only).\n\n**Returns:** `{success: true, text: \"Record added\"}`.\n\n**Auth:** Session/API key. Zone ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `Type must be one of: ...` — `type` not in allowlist.\n- `invalid or missing domain or record id` — zone not found / not owned.\n- Content-format validation failure (`text` describes the issue).\n\n**Related calls:**\n- **Find new record id:** `getDnsDomain`.\n- **Edit later:** `updateDnsRecord`.\n- **Delete:** `deleteDnsRecord`.\n"
      },
      "delete": {
        "tags": [
          "DNS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The DNS domain ID to delete. Use the `id` from `GET /dns` to identify the domain.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteDnsDomain",
        "summary": "Permanently delete a DNS zone and every record it contains",
        "description": "Removes the zone identified by path `id` AND every record it contains from PowerDNS in a single transaction. **Permanent — no soft-delete, no undo.** Any service relying on these records (web, mail, SPF/DKIM, third-party domain verifications, ACME challenges) will start failing as resolver caches expire (per-record TTL, default 86400s). **Note:** this only deletes the hosted zone on InterServer's nameservers — it does not affect registrar delegation. If `cdns1`/`cdns2` are still delegated at the registrar, queries will return NXDOMAIN/SERVFAIL until delegation is changed or the zone is recreated. Sibling ops: `deleteDnsRecord` (delete one record only), `addDnsDomain` (recreate), `updateDomainNameservers` (change registrar delegation).\n\n**Path param:**\n- `id` (string, required) — zone ID from `getDnsList`.\n\n**Returns:** `{success: true, text: \"Domain deleted\"}`.\n\n**Side effects:**\n- Deletes every `records` row with `domain_id={id}`.\n- Deletes the `domains` row.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `invalid or missing domain or record id` — zone not found / not owned.\n\n**Related calls:**\n- **Delete one record only:** `deleteDnsRecord`.\n- **Recreate the zone:** `addDnsDomain`.\n- **Update registrar delegation:** `updateDomainNameservers` (Domains tag).\n"
      },
      "parameters": [
        {
          "examples": {
            "DnsDomainIdExample": {
              "value": "472"
            }
          },
          "name": "id",
          "description": "The DNS Domain ID.",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/dns/{domainId}/{recordId}": {
      "summary": "DNS Record Management",
      "description": "Manage a DNS Record for the given Domain.",
      "post": {
        "requestBody": {
          "description": "The request data to update a dns record.",
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DnsUpdateRecord"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DnsUpdateRecord"
              }
            }
          },
          "required": true
        },
        "tags": [
          "DNS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateDnsRecord",
        "summary": "Replace values on an existing DNS record (name, type, content, ttl, priority)",
        "description": "Replaces the record identified by `recordId` within zone `domainId` with new values. **TTL caveat:** the change is written to PowerDNS immediately, but already-cached resolver answers persist until the previous record's TTL expires — plan TTL down ahead of a clean cutover. Type is validated against the global `$rtypes` allowlist; content is validated against the record type. Sibling ops: `getDnsDomain` (read), `addDnsRecord` (create), `deleteDnsRecord`.\n\n**Path params:**\n- `domainId` (integer, required) — zone ID from `getDnsList.id`.\n- `recordId` (integer, required) — record ID from `getDnsDomain.id`.\n\n**Body fields (form or JSON, schema `DnsUpdateRecord`):**\n- `name` (string, required) — FQDN at/below zone apex.\n- `type` (string, required) — one of the allowed PowerDNS types.\n- `content` (string, required) — value matching `type`.\n- `ttl` (integer, required) — seconds.\n- `prio` (integer, required) — MX/SRV priority (`0` otherwise).\n\n**Returns:** `{success: true, text: \"domain record updated\"}`.\n\n**Auth:** Session/API key. Zone ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `Type must be one of: ...` — `type` not in `$rtypes`.\n- `invalid or missing domain or record id` — zone/record not found / not owned.\n- Content-format validation text — `validate_input()` failure.\n\n**Related calls:**\n- **Read first:** `getDnsDomain`.\n- **Delete:** `deleteDnsRecord`.\n- **Create new:** `addDnsRecord`.\n"
      },
      "delete": {
        "tags": [
          "DNS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteDnsRecord",
        "summary": "Permanently delete one DNS record from a zone — zone itself is preserved",
        "description": "Removes the record identified by `recordId` from zone `domainId`. The zone itself remains intact — only the one record is dropped. **Permanent** — applied to PowerDNS immediately, but resolvers continue to answer with cached values until the previous TTL expires. Use to surgically remove an A/AAAA/MX/TXT etc. record; to drop the entire zone and all its records, use `deleteDnsDomain`. Sibling ops: `getDnsDomain` (verify after deletion), `deleteDnsDomain`, `updateDnsRecord`.\n\n**Path params:**\n- `domainId` (integer, required) — zone ID from `getDnsList.id`.\n- `recordId` (integer, required) — record ID from `getDnsDomain.id`.\n\n**Returns:** `{success: true, text: \"domain record deleted\"}`.\n\n**Auth:** Session/API key. Zone ownership enforced via `get_dns_domain($domainId)`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `invalid or missing domain or record id` — zone/record not found or not owned.\n- `error removing domain record` — underlying DB delete failed.\n\n**Related calls:**\n- **Verify after delete:** `getDnsDomain`.\n- **Recreate:** `addDnsRecord`.\n- **Delete entire zone instead:** `deleteDnsDomain`.\n"
      },
      "parameters": [
        {
          "name": "domainId",
          "description": "The DNS domain ID. Use the `id` from `GET /dns` to identify the domain.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        },
        {
          "name": "recordId",
          "description": "The DNS record ID within the domain. Use the record `id` from `GET /dns/{id}` to identify the record.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DomainRow"
                  }
                },
                "examples": {
                  "DomainsExample": {
                    "value": [
                      {
                        "domain_id": "418295",
                        "domain_hostname": "unixsrv10.com",
                        "domain_expire_date": "",
                        "cost": "11.00",
                        "domain_status": "canceled"
                      },
                      {
                        "domain_id": "592337",
                        "domain_hostname": "detain.dev",
                        "domain_expire_date": "2023-08-14T00:59:38.000Z",
                        "cost": "18.00",
                        "domain_status": "active"
                      }
                    ]
                  }
                }
              }
            },
            "links": {
              "getDomainInfo": {
                "operationId": "getDomainInfo",
                "parameters": {
                  "id": "$response.body#/0/domain_id"
                },
                "description": "Use the `domain_id` from any item in the response array to fetch full service details via `GET /domains/{id}`."
              }
            },
            "description": "The listing of `Domains` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainsList",
        "summary": "List every domain registration on the account with billing and registration metadata",
        "description": "Enumerates every domain registration owned by the authenticated customer — hostname, expiry, recurring cost, status. The canonical entry point for finding a `domain_id` to pass into other Domains endpoints. Empty array means the account has no domains (not an error). Sibling ops: `getDomainInfo`, `getNewDomain`, `getDomainLookup`, `addDomain`, `CancelDomain`.\n\n**Path/Query/Body:** None.\n\n**Returns:** Array of `DomainRow`:\n- `domain_id` (string) — canonical id; pass to every `/domains/{id}/*` endpoint.\n- `domain_hostname` (string) — registered FQDN.\n- `domain_expire_date` (string ISO 8601 or empty) — registry expiry; empty when not yet activated or unknown.\n- `cost` (decimal string) — recurring renewal cost in the domain's billing currency.\n- `domain_status` (string enum) — `pending` / `active` / `expired` / `canceled` / `pending-transfer`.\n\n**Auth:** Session/API key. Filtered by `domain_custid`.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Per-domain detail:** `getDomainInfo`.\n- **Manage:** `getDomainContact` / `updateDomainContact`, `getDomainNameservers` / `updateDomainNameservers`, `getDomainDnssec` / `addDomainDnssec`, `getDomainWhoisPrivacy` / `updateDomainWhoisPrivacy`.\n- **Renew / transfer:** `getDomainRenewal` / `postDomainRenewal`, `getDomainTransfer`.\n- **Order a new domain:** `getDomainLookup` → `getNewDomain` → `addDomain`.\n- **Cancel:** `CancelDomain`.\n"
      }
    },
    "/domains/lookup/{name}": {
      "get": {
        "tags": [
          "Domains"
        ],
        "parameters": [
          {
            "name": "name",
            "description": "The full domain name to look up (for example `example.com`).",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainLookupResponse"
                }
              }
            },
            "links": {
              "addDomain": {
                "operationId": "addDomain",
                "description": "Use the pricing and field metadata from this lookup response to populate the order request for `POST /domains/order`."
              }
            },
            "description": "Availability, pricing, and field metadata for the requested domain."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/InvalidDomain"
          }
        },
        "operationId": "getDomainLookup",
        "summary": "Check availability, premium status, and pricing for a specific domain",
        "description": "Looks up a single FQDN against OpenSRS: returns availability, premium-name flag, current new/renewal/transfer prices, per-TLD order field metadata, and multi-currency quotes. **Public** endpoint — no auth required (rate-limited via `domainlookup` cache). Repeated lookups within a short window may return cached results from the `domainlookup` table. Use as step 1 of an order: discover availability and pricing, then call `addDomain` to commit. Sibling ops: `getDomainSearch` (suggestions), `getNewDomain` (catalog), `addDomain`, `postDomainSearch`.\n\n**Path param:**\n- `name` (string, required) — full FQDN (e.g. `example.com`).\n\n**Returns** (schema `DomainLookupResponse`):\n- `available` (bool) — registerable now.\n- `premium` (bool) — premium-name pricing (often > $100).\n- `website` (bool) — same hostname is already a webhosting service on this account.\n- `domain_service` (bool) — same hostname is already a domain on this account.\n- `service` (object) — `services_id`, `services_name`, `services_cost`, `services_field1` (TLD), `services_module`.\n- `whois_privacy` (bool) — privacy add-on available for this TLD.\n- `new`, `renewal`, `transfer` (float) — base USD prices (with profit markup).\n- `fields` (object) — per-TLD order form schema (labels, options, current account values).\n- `currencies` (object) — `{<code>: {services_cost, new, renewal, transfer}}` converted to each enabled currency.\n\n**Auth:** Public (no auth required).\n\n**Errors:**\n- `422 Invalid Domain` — `valid_domain()` rejected input.\n\n**Related calls:**\n- **Brainstorm alternatives:** `getDomainSearch`.\n- **Place order:** `addDomain` with the resolved `service.services_id` and `fields`.\n- **TLD catalog:** `getNewDomain`.\n"
      }
    },
    "/domains/search/{name}": {
      "get": {
        "tags": [
          "Domains"
        ],
        "parameters": [
          {
            "name": "name",
            "description": "The base domain name to search (for example `example` or `example.com`).",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainSearchResponse"
                }
              }
            },
            "description": "Suggested and lookup results for the supplied search term."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/DomainSearchNoResults"
          }
        },
        "operationId": "getDomainSearch",
        "summary": "Get registrar-suggested domain alternatives and bulk availability for a search term",
        "description": "Returns registrar-suggested alternatives plus bulk availability data from OpenSRS for the supplied search term. Useful when a customer is brainstorming names. Pair with `getDomainLookup` to get full pricing and per-TLD order fields for any specific chosen result. Sibling ops: `postDomainSearch`, `getDomainLookup`, `getNewDomain`, `addDomain`.\n\n**Path param:**\n- `name` (string, required) — search term (e.g. `example` or `example.com`).\n\n**Returns** (schema `DomainSearchResponse`):\n- `success` (bool) — registrar call succeeded.\n- `response_text` (string) — registrar response message.\n- `response_time` (float) — registrar latency (seconds).\n- `lookup` (array) — exact-match availability across the searched TLD set.\n- `suggest` (array) — registrar's recommended alternative names with availability.\n- `tlds` (array) — TLDs queried.\n\n**Auth:** Public.\n\n**Errors:**\n- `422 Invalid Search Response!` — registrar returned no usable results.\n\n**Related calls:**\n- **Single-domain detail:** `getDomainLookup`.\n- **One-shot order preview from a search term:** `postDomainSearch`.\n"
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "parameters": [
          {
            "name": "name",
            "description": "The base domain name to search (for example `example` or `example.com`).",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "Domain availability and pricing check results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postDomainSearch",
        "summary": "Get the full order form data for a hostname in one round-trip (search → order preview)",
        "description": "Returns the complete order-form payload — pricing, service catalog entry, per-TLD order fields — for the hostname in a single POST. Equivalent to calling `getDomainLookup` + `getNewDomain` + `putDomains` and merging the results, but with one round-trip. The path `name` is moved server-side into `$_POST['hostname']` and passed to `getOrderDomainData(true)`. Sibling ops: `getDomainSearch`, `getDomainLookup`, `getNewDomain`, `addDomain`.\n\n**Path param:**\n- `name` (string, required) — hostname (e.g. `example.com`).\n\n**Body:** None.\n\n**Returns:** Combined order-data response — pricing, service catalog entry, form fields ready to populate for `addDomain`.\n\n**Auth:** Session/API key (path is `client_api`, but called publicly).\n\n**Errors:**\n- `4xx` — hostname cannot be resolved to a TLD service.\n\n**Related calls:**\n- **Place order:** `addDomain` with the returned fields.\n"
      },
      "parameters": [
        {
          "name": "name",
          "description": "The base domain name to search (for example `example` or `example.com`).",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/order": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainOrder"
                }
              }
            },
            "description": "Domain registration order information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewDomain",
        "summary": "Read the buyable domain TLD service catalog and Whois privacy pricing",
        "description": "Returns the catalog of buyable TLD services and the base Whois-privacy add-on pricing. Use to resolve a hostname's TLD to a `service_id` for ordering, or to render a TLD picker. Pair with `getDomainLookup` for per-domain pricing and `addDomain` to commit. Sibling ops: `getDomainLookup`, `putDomains`, `patchDomains`, `addDomain`.\n\n**Path/Query/Body:** None.\n\n**Returns** (schema `DomainOrder`):\n- `whoisPrivacyCost` (float) — base per-year privacy cost (`OPENSRS_PRIVACY_COST` constant).\n- `whoisPrivacyCostTotal` (object) — privacy cost per TLD multiplied by that TLD's term length: `{<tld>: <total-cost>}`.\n- `services` (object) — `{<services_id>: {services_name, services_cost, services_field1 (tld), ...}}`. `services_ourcost` is stripped.\n- `tldServices` (object) — TLD → `services_id` lookup map (e.g. `{\"com\": 100, \"net\": 101, \"io\": 234}`).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Per-domain pricing:** `getDomainLookup`.\n- **Preview order fields:** `putDomains`.\n- **Validate fields:** `patchDomains`.\n- **Place order:** `addDomain`.\n"
      },
      "put": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "description": "Validate Domain Order response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putDomains",
        "summary": "Preview per-TLD field requirements for a domain order — no commit",
        "description": "Pre-flight that returns the per-TLD field schema required to register or transfer the supplied hostname. The schema varies significantly by TLD: `.us` requires nexus codes, `.ca` requires CIRA legal type, `.eu` has residency rules, `.fr` requires VAT for orgs, etc. Values pre-populate from the account profile when possible. No commit — use `patchDomains` to validate filled values, then `addDomain` to place the order. Sibling ops: `getNewDomain`, `getDomainLookup`, `patchDomains`, `addDomain`.\n\n**Body fields:**\n- `hostname` (string, required) — FQDN.\n- `type` (string, optional, default `register`) — `register` or `transfer`.\n- `coupon` (string, optional) — coupon code.\n\n**Returns:** `{domainFields: {<field_name>: {label, type, options, value, required, ...}}}` — schema for the dynamic order form.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `400 Missing hostname parameter`.\n- `400 Unable to determine service type for this domain TLD.` — unknown TLD.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Catalog first:** `getNewDomain`.\n- **Validate filled values:** `patchDomains`.\n- **Place order:** `addDomain`.\n"
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Register or transfer a domain. Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addDomain",
        "summary": "Place a new domain registration or transfer order, generate billing invoice",
        "description": "Places a new domain registration or transfer order. Resolves the TLD to a `services_id`, runs `validate_buy_domain()` (hostname, TLD service, fields, coupon, whois-privacy add-on), then calls `place_buy_domain()` to create the `Repeat_Invoice` recurring billing row, generate the initial `invoices` row, and (when `whois_privacy=enable`) an additional add-on `Repeat_Invoice` for privacy. **Real money** — call `putDomains` then `patchDomains` first to preview and validate. Sibling ops: `getDomainLookup`, `getNewDomain`, `putDomains`, `patchDomains`, `initiatePayment`.\n\n**Body fields (JSON or form):**\n- `hostname` (string, required) — FQDN to register or transfer.\n- `type` (string, optional, default `register`) — `register` or `transfer`.\n- `whois_privacy` (string, optional) — `enable` to add the privacy add-on (separate recurring invoice).\n- `coupon` (string, optional) — coupon code.\n- All per-TLD contact/registration fields from `putDomains.domainFields` (registrant contact details, TLD-specific fields like nexus codes, EPP `auth_info` for transfers, etc.).\n\n**Returns** (schema `ServiceOrderPostResponse`): `{total_cost, iid, iids, real_iids, serviceid (new domain_id), invoice_description, cj_params, payUrl}` — pass `real_iids` to `initiatePayment` to fund the order.\n\n**Side effects:**\n- Inserts `domains` service row in `pending` status.\n- Inserts `repeat_invoices` row for recurring renewal.\n- Inserts `invoices` row for the first-period charge.\n- When `whois_privacy=enable`: inserts a separate add-on `repeat_invoices` row + its initial invoice.\n- For transfers: stores `auth_info` and marks `service_extra` as `transfer`.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `400 Missing hostname parameter`.\n- `400 Unable to determine service type for this domain TLD.`.\n- `401` — unauthenticated.\n- `422` — validation failure (e.g. coupon invalid, required TLD field missing, hostname not registerable). Response body is the combined `errors` array from `validate_buy_domain()`.\n\n**Related calls:**\n- **Prerequisites:** `getDomainLookup` → `getNewDomain` → `putDomains` → `patchDomains`.\n- **Pay:** `initiatePayment` with `real_iids`.\n- **Verify activation:** `getDomainInfo` (poll for `domain_status='active'`).\n- **Track in-progress transfer:** `getDomainTransfer`.\n- **For pending transfers needing EPP/auth_info:** `updateDomainContact` (set `auth_info`).\n\n**Example happy path (register):**\n```text\nGET /apiv2/domains/lookup/example.com           -> available, pricing, fields\nPUT /apiv2/domains/order { hostname, type }     -> domainFields\nPATCH /apiv2/domains/order { hostname, ...fields } -> \"success\"\nPOST /apiv2/domains/order { hostname, type, ...fields } -> { serviceid, real_iids }\nGET /apiv2/billing/pay/cc/{real_iids[0]}        -> pay\nGET /apiv2/domains/{serviceid}                  -> poll until domain_status==\"active\"\n```\n"
      },
      "patch": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "description": "Validate Domain order response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "patchDomains",
        "summary": "Validate posted domain-order field values before committing — dry run",
        "description": "Validates posted contact/registration field values via `validate_domain_fields()`. Enforces per-TLD requirements (nexus codes, postal formats, registrant org rules, EPP `auth_info` syntax for transfers, etc.). Use as the last step before `addDomain` to surface form errors cheaply. No commit — no invoice, no service record. Sibling ops: `putDomains`, `addDomain`, `getDomainLookup`, `getNewDomain`.\n\n**Body fields:**\n- `hostname` (string, required).\n- `type` (string, optional, default `register`) — `register` or `transfer`.\n- All per-TLD fields from `putDomains.domainFields`.\n\n**Returns:** `\"success\"` (string) when all fields validate; otherwise an `errors` object describing the failing fields and per-field validation messages.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `400 Missing hostname parameter`.\n- `400 Unable to determine service type for this domain TLD.`.\n- `401` — unauthenticated.\n- Validation error object — fields-level failures.\n\n**Related calls:**\n- **Schema:** `putDomains` (returns the field set to validate).\n- **Commit:** `addDomain`.\n"
      }
    },
    "/domains/{id}": {
      "get": {
        "tags": [
          "Domains"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Domain"
                }
              }
            },
            "links": {
              "getDomainContact": {
                "operationId": "getDomainContact",
                "parameters": {
                  "id": "$response.body#/serviceInfo/domain_id"
                },
                "description": "Retrieve the registrant and admin contact details for this domain."
              },
              "getDomainDnssec": {
                "operationId": "getDomainDnssec",
                "parameters": {
                  "id": "$response.body#/serviceInfo/domain_id"
                },
                "description": "Retrieve the DNSSEC DS records for this domain."
              },
              "getDomainInvoices": {
                "operationId": "getDomainInvoices",
                "parameters": {
                  "id": "$response.body#/serviceInfo/domain_id"
                },
                "description": "Retrieve the billing invoices for this domain order."
              },
              "getDomainWhoisPrivacy": {
                "operationId": "getDomainWhoisPrivacy",
                "parameters": {
                  "id": "$response.body#/serviceInfo/domain_id"
                },
                "description": "Retrieve the Whois privacy status for this domain."
              }
            },
            "description": "Domain Information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainInfo",
        "summary": "Read full billing, registrar, and service detail for one domain",
        "description": "Returns the full `ViewDomain` payload for one domain — billing summary, registration status, lock state, expiry date, contact summary, and `client_links` for related dashboard actions. Read-only. Internal `admin_links`, `settings`, `csrf` are stripped before return. Use to render a domain detail page, verify ownership before mutating, or poll `domain_status` after `addDomain`. Sibling ops: `getDomainContact`, `getDomainNameservers`, `getDomainDnssec`, `getDomainWhoisPrivacy`, `getDomainInvoices`, `updateDomainInfo`, `CancelDomain`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns** (schema `Domain`):\n- `serviceInfo` — `domain_id`, `domain_hostname`, `domain_status`, `domain_expire_date`, lock state, registrar metadata.\n- `serviceType` — TLD service row.\n- `client_links` (array) — `{name, link, icon}` for renew/transfer/contact/DNSSEC/whois-privacy actions. URLs pre-resolved.\n\n**Auth:** Session/API key. Ownership enforced via `domain_custid`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n\n**Related calls:**\n- **Contact:** `getDomainContact` / `updateDomainContact`.\n- **Nameservers:** `getDomainNameservers` / `updateDomainNameservers`.\n- **DNSSEC:** `getDomainDnssec` / `addDomainDnssec` / `deleteDomainDnssec`.\n- **Whois privacy:** `getDomainWhoisPrivacy` / `updateDomainWhoisPrivacy`.\n- **Billing:** `getDomainInvoices`, `getDomainRenewal` / `postDomainRenewal`.\n- **Transfer status:** `getDomainTransfer`.\n- **Cancel:** `CancelDomain`.\n"
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateDomainInfo",
        "summary": "POST mutation hook for the domain detail page (use dedicated ops where possible)",
        "description": "Account-level write-back hook for the domain service record. Runs the same `View::go()` handler as `getDomainInfo` — it does NOT push registrar-side changes by itself. **For specific changes use the dedicated endpoints** — they push to OpenSRS where appropriate. Sibling ops: `getDomainInfo`, `updateDomainContact`, `updateDomainNameservers`, `addDomainDnssec`, `updateDomainWhoisPrivacy`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body:** Form fields matching the domain service record.\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n\n**Prefer these dedicated endpoints:**\n- **Registrant/admin contact:** `updateDomainContact` (pushes to OpenSRS).\n- **Nameservers:** `updateDomainNameservers`, `addDomainNameserver`, `deleteDomainNameserver`.\n- **DNSSEC:** `addDomainDnssec`, `deleteDomainDnssec`.\n- **Whois privacy:** `updateDomainWhoisPrivacy`.\n- **Renew:** `postDomainRenewal`.\n- **Cancel:** `CancelDomain`.\n"
      },
      "delete": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/DomainsCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "CancelDomain",
        "summary": "Cancel a domain order in the billing system to stop auto-renewals",
        "description": "Stops billing and auto-renewal for a domain in the customer account by setting the service to `canceled` via the shared `Billing\\CancelService::go($id)` flow with `module='domains'`. **Important:** this only stops billing on InterServer's side — the domain registration at the registrar (OpenSRS) typically remains active until its current expiration date. To release the domain back to the public pool, let it expire OR submit a release request via support ticket. Sibling ops: `getDomainInfo` (verify status), `getDomainsList`, `postDomainRenewal` (re-activate before expiry).\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns:** `DomainsCancelResponse` — confirmation envelope.\n\n**Side effects:**\n- Sets `domain_status='canceled'`.\n- Marks the `repeat_invoices` row non-renewing — no future renewal invoices generated.\n- Does **not** call the registrar — the registration remains active at OpenSRS until natural expiry.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — already canceled.\n\n**Related calls:**\n- **Re-activate before expiry:** `postDomainRenewal`.\n- **Verify status:** `getDomainInfo`.\n- **Sibling cancels on other modules:** `VPSCancel`, `mailCancel`, `webhostingCancel`, etc. (same `CancelService` handler).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/contact": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainContactDetails"
                }
              }
            },
            "description": "The registrant/admin contact details for the domain."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainContact",
        "summary": "Read the current registrant/admin/tech/billing contact field set for a domain",
        "description": "Returns the current contact field set (registrant/admin/tech/billing) with current values for the domain — schema and values mirror what was set at registration. For pending transfer services, the response also includes a `transfer` selector and the EPP `auth_info` code so the client can resubmit. Read-only. Sibling ops: `updateDomainContact` (push changes to OpenSRS), `getDomainInfo`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns** (schema `DomainContactDetails`):\n- `firstname`, `lastname`, `email`.\n- `address`, `address2`, `address3`, `city`, `state`, `zip`, `country`.\n- `phone`, `fax` (E.164 format expected).\n- `company` (optional).\n- `auth_info` (string) — EPP/transfer code (present on transfer services).\n- `transfer` (string `yes`/`no`) — selector for pending transfer services.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n\n**Related calls:**\n- **Update:** `updateDomainContact`.\n- **Transfer status:** `getDomainTransfer`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainContactDetails"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DomainContactDetails"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateDomainContact",
        "summary": "Update registrant/admin contact details and push them to OpenSRS",
        "description": "Pushes updated contact data to the registrar via OpenSRS `provModify`, applied to admin/tech/billing/owner contact roles via `also_apply_to`. Active services apply changes immediately at the registrar; pending services may trigger `queue_process_payment` when the order is paid. **Domain must not be locked** — locked domains return an error directing the user to unlock first. **Note:** registrant-name changes on some TLDs (e.g. `.com`, `.net`) require a 60-day transfer lock per ICANN rules. Sibling ops: `getDomainContact`, `getDomainInfo`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body fields (JSON or multipart, schema `DomainContactDetails`):**\nSame fields returned by `getDomainContact`: `firstname`, `lastname`, `email`, `address`/`2`/`3`, `city`, `state`, `zip`, `country`, `phone`, `fax`, `company`, optional `auth_info`, optional `transfer`.\n\n**Returns:** `SuccessTextResponse`.\n\n**Side effects:**\n- Calls OpenSRS `provModify` with `also_apply_to=admin,tech,billing,owner`.\n- May trigger ICANN 60-day transfer lock for registrant-name changes on legacy TLDs.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — domain is locked (unlock first).\n- Registrar error (e.g. OpenSRS error code 465 / 466) surfaced as 4xx.\n\n**Related calls:**\n- **Read first:** `getDomainContact`.\n- **Transfer status:** `getDomainTransfer`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/dnssec": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainDnssecRecords"
                }
              }
            },
            "description": "DNSSEC records currently applied to the domain."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainDnssec",
        "summary": "Read the DNSSEC DS record set currently registered with the registrar",
        "description": "Returns the DNSSEC DS record set currently registered for the domain at OpenSRS. Empty array means DNSSEC is not configured. Use to mirror existing settings or as a baseline before `addDomainDnssec` (which replaces the set). Sibling ops: `addDomainDnssec`, `deleteDomainDnssec`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns** (schema `DomainDnssecRecords`):\n- `records` (array) — DS entries:\n  - `algorithm` (integer) — DNSKEY algorithm (e.g. 8 for RSASHA256, 13 for ECDSAP256SHA256).\n  - `key_tag` (integer, < 65536).\n  - `digest_type` (integer) — `1` (SHA-1), `2` (SHA-256), `3` (GOST), `4` (SHA-384).\n  - `digest` (string, hex) — length depends on `digest_type`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n\n**Related calls:**\n- **Replace records:** `addDomainDnssec`.\n- **Clear all records:** `deleteDomainDnssec`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainDnssecRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DomainDnssecRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "addDomainDnssec",
        "summary": "Register DNSSEC DS records on the domain at OpenSRS",
        "description": "Registers one or more DNSSEC DS records at the registrar. Body uses parallel arrays indexed per record. **Propagation caveat:** DNSSEC publication is asynchronous at the registry — a 200 here does not guarantee the records have propagated; re-call `getDomainDnssec` to verify. **Sets, not adds:** this replaces the full DS record set in one transaction; to remove all DS records use `deleteDomainDnssec`. Sibling ops: `getDomainDnssec`, `deleteDomainDnssec`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body fields (JSON or multipart, parallel arrays — `algorithm[0]` pairs with `key_tag[0]`, etc., schema `DomainDnssecRequest`):**\n- `algorithm[]` (integer) — DNSKEY algorithm (e.g. 8 = RSASHA256, 13 = ECDSAP256SHA256).\n- `key_tag[]` (integer) — must be < 65536.\n- `digest_type[]` (integer) — `1` (SHA-1, 40 hex chars), `2` (SHA-256, 64), `3` (GOST, 64), `4` (SHA-384, 96).\n- `digest[]` (string) — hex digest; length must match `digest_type[i]`.\n\n**Returns:** `SuccessTextResponse` on registrar confirmation.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"` or registrar refused.\n- `422` — `key_tag >= 65536` or digest length mismatch.\n\n**Related calls:**\n- **Verify propagation:** `getDomainDnssec`.\n- **Clear all records:** `deleteDomainDnssec`.\n"
      },
      "delete": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteDomainDnssec",
        "summary": "Clear all DNSSEC DS records on the domain (disable DNSSEC at the registrar)",
        "description": "Disables DNSSEC at the registrar by removing the entire DS record set in one call. **Propagation caveat:** DNSSEC removal can fail at the registry even after a 200 response — propagation is asynchronous; re-check with `getDomainDnssec` to confirm. To remove records selectively, replace the set via `addDomainDnssec` instead. Sibling ops: `getDomainDnssec`, `addDomainDnssec`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body:** None — removes the full DS record set.\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n- Registrar error surfaced as 4xx.\n\n**Related calls:**\n- **Verify propagation:** `getDomainDnssec`.\n- **Replace records selectively:** `addDomainDnssec`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/invoices": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainInvoices",
        "summary": "List all billing invoices scoped to one domain order",
        "description": "Returns the billing history for one domain — initial registration/transfer invoice, recurring renewal invoices, Whois privacy add-on invoices. Extends `Billing\\InvoicesList::go()` with `module='domains'`. Use to render a per-domain billing-history view or find an unpaid renewal/privacy invoice to pass to `initiatePayment`. Sibling ops: `getDomainInfo`, `postDomainRenewal`, `updateDomainWhoisPrivacy`, `initiatePayment`, `getBillingInvoice`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns:** `ChargeInvoiceRows` — array of `{id, amount, paid, description, date, due_date, currency, module: \"domains\", service: <id>}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Service` — `id` not owned by caller.\n\n**Related calls:**\n- **Pay an unpaid invoice:** `initiatePayment`.\n- **Renew:** `postDomainRenewal`.\n- **Account-wide history:** `getBillingInvoices`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/renew": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainRenewal",
        "summary": "Read renewal pricing, expiry, and whether a renewal invoice already exists",
        "description": "Returns renewal pricing, current expiry, Whois privacy availability, and whether an unpaid renewal invoice already exists for the domain. Use before triggering `postDomainRenewal` to render a renewal form and prevent duplicate invoices. Costs are converted to the customer's preferred currency. Sibling ops: `postDomainRenewal`, `getDomainInvoices`, `getDomainInfo`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns:**\n- `renewCost` (float) — renewal cost in `currency`.\n- `whoisCost` (float) — Whois privacy renewal cost.\n- `whoisAvailable` (bool) — privacy supported on this TLD.\n- `currency` (string), `currencySymbol` (string).\n- `expiryDate` (string).\n- `alreadyInvoiced` (bool) — a renewal `Repeat_Invoice` already produced an invoice.\n- `invoicePaid` (bool) — whether that invoice is paid.\n- `tld` (string).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n- `4xx` — renewal not available for this TLD.\n\n**Related calls:**\n- **Submit renewal:** `postDomainRenewal`.\n- **Pay existing renewal invoice:** `getDomainInvoices` → `initiatePayment`.\n"
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postDomainRenewal",
        "summary": "Submit a domain renewal request and generate the renewal invoice",
        "description": "Generates a renewal invoice for the domain (and optionally the Whois privacy add-on). Updates the domain's `Repeat_Invoice` cost/frequency/currency to the current price, then calls `Repeat_Invoice::invoice()` to produce a fresh invoice. **Real money.** If a prior unpaid renewal invoice already exists, returns an error directing the user to pay that one instead — prevents double-billing. Renewal is not supported for some TLDs. Sibling ops: `getDomainRenewal`, `getDomainInvoices`, `initiatePayment`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body fields:**\n- `whois_privacy` (string, optional) — `enable` to add or keep the privacy add-on; otherwise the existing privacy `Repeat_Invoice` is marked `deleted=1` on renewal.\n\n**Returns:** `{text, invoices, invoiceIds, payUrl}` — pass `invoiceIds` to `initiatePayment` to settle.\n\n**Side effects:**\n- Updates `repeat_invoices` cost/frequency/currency.\n- Inserts a new `invoices` row for the renewal period.\n- When `whois_privacy=enable`: extends the privacy add-on `repeat_invoices` and creates its renewal invoice.\n- When `whois_privacy` not enabled: marks the existing privacy `repeat_invoices` row `deleted=1`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`, or unpaid renewal invoice already exists, or already-paid renewal exists.\n- Registrar errors surfaced as 4xx.\n\n**Related calls:**\n- **Preview:** `getDomainRenewal`.\n- **Pay:** `initiatePayment` with the returned `invoiceIds`.\n- **Cancel auto-renew:** `CancelDomain`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/transfer": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainTransfer",
        "summary": "Read OpenSRS transfer status for an in-progress domain transfer order",
        "description": "Returns the OpenSRS transfer state for a domain order flagged as a transfer in `service_extra`. Use to poll an in-progress transfer; `pending_owner` means the customer must click the approval link in the email sent by OpenSRS to the registrant. Sibling ops: `postDomainTransfer` (re-poll), `getDomainContact` (set `auth_info`), `addDomain` (initiate new transfer).\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns:**\n- When not a transfer: `{transfer: false, message: \"...\"}`.\n- When a transfer: `{transfer: true, info: {status, statusText, type, transferrable, reason}}` where `status` is one of:\n  - `pending` — submitted to OpenSRS, awaiting state change.\n  - `pending_owner` — **customer action required** (approve the OpenSRS email).\n  - `pending_admin` — InterServer staff review.\n  - `pending_registry` — registry processing.\n  - `declined` — transfer rejected (see `reason`).\n  - `completed` — transfer landed.\n  - `undef` — unknown.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n- Registrar communication failures returned as errors.\n\n**Related calls:**\n- **Re-poll:** `postDomainTransfer`.\n- **Update auth_info:** `updateDomainContact`.\n- **Initiate new transfer:** `addDomain` with `type=transfer`.\n"
      },
      "post": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postDomainTransfer",
        "summary": "Re-poll OpenSRS transfer status for a domain order via POST",
        "description": "Re-polls OpenSRS transfer state. Behaves identically to `getDomainTransfer` (same `go()` handler) — provided so dashboards can refresh via a form-action pattern. **This endpoint does not initiate transfers** — to start a transfer, use `addDomain` with `type=transfer`. Sibling ops: `getDomainTransfer`, `addDomain` (initiate), `getDomainContact` (set `auth_info`).\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body:** None.\n\n**Returns:** Same payload as `getDomainTransfer`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n\n**Related calls:**\n- **Read:** `getDomainTransfer`.\n- **Initiate new transfer:** `addDomain` with `type=transfer`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/welcome_email": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainsWelcomeEmail",
        "summary": "Resend the domain welcome email with registration details and management instructions",
        "description": "Resends the domain welcome email (registration details, management instructions, EPP code where applicable) to the customer's address on file. Idempotent — safe to call multiple times. Sibling welcome-email endpoints: `getVpsWelcomeEmail`, `getWebsitesWelcomeEmail`, `getMailWelcomeEmail`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body:** None.\n\n**Returns:** `{text: \"Welcome Email has been resent.\"}`.\n\n**Side effects:**\n- Sends an email to the account's billing email address.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Service Passed` — `id` not owned by caller.\n- `409 Service is not active` — `domain_status != \"active\"`.\n\n**Related calls:**\n- **Domain detail:** `getDomainInfo`.\n- **Contact info:** `getDomainContact`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/whois": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainWhoisPrivacy",
        "summary": "Read Whois privacy availability, current state, and add-on pricing for a domain",
        "description": "Returns Whois privacy state for the domain — whether the TLD supports privacy, whether it's currently enabled at OpenSRS, and the add-on cost. Some TLDs (e.g. `.us`, `.uk`, country-code variants) do not allow privacy regardless of pricing. Sibling op: `updateDomainWhoisPrivacy` (order/enable/disable).\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns:**\n- `available` (bool) — privacy supported for this TLD (via `get_domain_tld_whois_privacy()`).\n- `cost` (float) — annual privacy cost in `currency`.\n- `currency` (string), `currencySymbol` (string).\n- `whoisPrivacy` (string enum) — `enabled` / `disabled` (live OpenSRS state).\n- `repeatInvoice` (object|null) — current privacy add-on `Repeat_Invoice` row, if one exists.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n\n**Related calls:**\n- **Order/enable/disable:** `updateDomainWhoisPrivacy`.\n- **Billing:** `getDomainInvoices`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainWhoisPrivacyRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DomainWhoisPrivacyRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateDomainWhoisPrivacy",
        "summary": "Order, enable, or cancel the Whois privacy add-on for a domain",
        "description": "Manages the Whois privacy add-on. Behavior branches on `action`:\n- **`order`**: creates an add-on `Repeat_Invoice` and emits the first invoice; pass the returned `payUrl` to the customer or use `initiatePayment` with `invoiceId`. **Real money.**\n- **`enable`**: activates Whois privacy at OpenSRS — call after the invoice is paid (calls `post_payment_processing_new`).\n- **`disableCancel`**: disables Whois privacy at OpenSRS and marks the add-on `Repeat_Invoice` `deleted=1`.\n- **(no action)**: returns current state — same shape as `getDomainWhoisPrivacy`.\n\nSibling ops: `getDomainWhoisPrivacy`, `getDomainInvoices`, `initiatePayment`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body fields (JSON or multipart, schema `DomainWhoisPrivacyRequest`):**\n- `action` (string, optional) — one of `order` / `enable` / `disableCancel`. Omit for status.\n\n**Returns:** (varies by action)\n- `order`: `{text, invoiceId, repeatInvoiceId, payUrl}`.\n- `enable` / `disableCancel`: `{text}`.\n- No action: `{whoisPrivacy, cost, currency, currencySymbol}`.\n\n**Side effects:**\n- `order`: inserts add-on `repeat_invoices` + `invoices` rows.\n- `enable`: OpenSRS `provModify` with privacy=on; calls `post_payment_processing_new`.\n- `disableCancel`: OpenSRS `provModify` with privacy=off; marks add-on `repeat_invoices.deleted=1`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"` or TLD doesn't support privacy.\n- `4xx` — no add-on found for `enable` / `disableCancel`.\n\n**Related calls:**\n- **Read state:** `getDomainWhoisPrivacy`.\n- **Pay the order invoice:** `initiatePayment` (`method=cc|paypal|...`).\n- **Renew with privacy:** `postDomainRenewal` with `whois_privacy=enable`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/floating_ips": {
      "get": {
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            },
            "links": {
              "getFloatingIpInfo": {
                "operationId": "getFloatingIpInfo",
                "description": "Use an ID from the response array to fetch full details via `GET /floating_ips/{id}`."
              }
            },
            "description": "The listing of `Floating IPs` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "getFloatingIpsList",
        "summary": "List all Floating IP services on the authenticated customer's account",
        "description": "Use to enumerate every Floating IP the caller owns before drilling into a specific one. Read-only; safe to call frequently. No params, no body. Returns an array of rows: `floating_ip_id`, `repeat_invoices_cost` (recurring price), `floating_ip_ip` (the portable IP), `floating_ip_target_ip` (the IP it currently routes to), `floating_ip_status` (active/pending/canceled/etc.), `services_name` (package label). Empty array if the account owns no Floating IPs. Errors: 401 if unauthenticated. Use returned IDs with `getFloatingIpInfo`, `postFloatingIpsChangeIp`, `getFloatingIpInvoices`, `getFloatingIpsWelcomeEmail`, or `floating_ipsCancel`. To order a new one see `getNewFloatingIp` / `addFloatingIp`.\n\nSibling ops: `getFloatingIpInfo`, `getNewFloatingIp` (catalog), `addFloatingIp` (order)."
      }
    },
    "/floating_ips/order": {
      "get": {
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "description": "Floating IP ordering options and pricing.",
                  "type": "object"
                }
              }
            },
            "description": "Available options and pricing for ordering a Floating IP."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewFloatingIp",
        "summary": "Get pricing and service-type options for ordering a new Floating IP",
        "description": "Use before showing a Floating IP order form, or before calling `addFloatingIp`, to discover which service types (`serviceTypes`) and prices (`packageCosts`, keyed by `services_id` in the customer's currency) are currently buyable. Read-only; no side effects. No params, no body. Returns `{ packageCosts: { <services_id>: <cost> }, serviceTypes: [ ... ] } `. Costs are `services.services_cost` filtered to `services_buyable=1` for module `floating_ips`. Errors: 401 if unauthenticated. Next steps: validate the chosen `serviceType` with `putFloating_ips`, then place the order with `addFloatingIp`. Floating IPs are portable IPv4 addresses that route to a target IP on one of the customer's active services.\n\nSibling ops: `putFloating_ips` (validate), `addFloatingIp` (commit), `getFloatingIpsList` (existing IPs)."
      },
      "put": {
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "description": "Validate Floating IPs order response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putFloating_ips",
        "summary": "Validate a Floating IP order and price it without charging the customer",
        "description": "Dry-run for `addFloatingIp` — runs `validate_buy_floating_ip` to apply coupons, compute intro/repeat pricing, and surface errors before committing. No charge, no service created. Body fields (form-encoded): `serviceType` (required, `services_id` from `getNewFloatingIp.packageCosts`), `coupon` (optional code). Returns `{ continue, errors, serviceType, serviceCost, originalCost, repeatServiceCost, password, introFrequency, coupon, couponCode }`. `continue=true` means the order would succeed; `continue=false` plus populated `errors[]` means it would not. Errors: 401 if unauthenticated; 422-style soft errors arrive in the `errors` array. Use the returned `serviceType` and `couponCode` when calling `addFloatingIp`.\nSibling ops: `getNewFloatingIp` (catalog), `addFloatingIp` (commit)."
      },
      "post": {
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Provision a floating IPv4 address. Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addFloatingIp",
        "summary": "Place a real Floating IP order, create billing records, and provision the service",
        "description": "Charges the customer and creates a new Floating IP service via `place_buy_floating_ip`. Validate first with `putFloating_ips` to avoid surprise failures. Body (form-encoded): `serviceType` (required, `services_id`), `coupon` (optional), `comment` (optional internal note). On success returns `{ continue:true, errors, total_cost, iid, iids, real_iids, serviceId, invoice_description, cj_params }` — `iid` is the master invoice ID, `serviceId` is the new `floating_ip_id`. On validation failure returns `{ continue:false, errors:[...] }` with no charge. Errors: 401 if unauthenticated; soft errors in `errors[]`. The newly-issued IP starts unassigned — point it at a target with `postFloatingIpsChangeIp` once the service is `active`.\n\nSibling ops: `getNewFloatingIp` (catalog), `putFloating_ips` (validate), `getFloatingIpInfo` (poll), `postFloatingIpsChangeIp` (route), `getBillingInvoice` + `initiatePayment` (settle invoice), `floating_ipsCancel`."
      }
    },
    "/floating_ips/{id}": {
      "get": {
        "tags": [
          "Floating_IPs"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The Floating IP service ID. Use the ID from `GET /floating_ips`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "description": "Floating IP service details.",
                  "type": "object"
                }
              }
            },
            "description": "Detailed Floating IP service information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getFloatingIpInfo",
        "summary": "Fetch full details for one Floating IP service, including current target IP",
        "description": "Use for a Floating IP detail screen, or to read `floating_ip_ip` / `floating_ip_target_ip` before calling `postFloatingIpsChangeIp`. Read-only. Path param `id` (integer, `floating_ip_id` from `getFloatingIpsList`). No body. Returns the `ViewFloatingIp.getDetails()` payload — service info, billing/cost summary, status, target IP, and `client_links` (action URLs the UI can render). Internal-only fields (`admin_links`, `settings`, `csrf`) are stripped. Errors: 401 if unauthenticated; effectively 404 / cross-customer hidden when `id` is not owned by the caller (`get_service` filters by custid). Siblings: `postFloatingIpsChangeIp`, `updateFloatingIpInfo`, `getFloatingIpInvoices`, `getFloatingIpsWelcomeEmail`, `floating_ipsCancel`."
      },
      "post": {
        "tags": [
          "Floating_IPs"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The Floating IP service ID. Use the ID from `GET /floating_ips`.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateFloatingIpInfo",
        "summary": "Update a Floating IP service's editable settings (label / metadata)",
        "description": "Stub edit endpoint that delegates to the same handler as `getFloatingIpInfo` — currently used for label/metadata edits surfaced by `ViewFloatingIp`. To re-route the IP to a different target use the dedicated `postFloatingIpsChangeIp` instead; this op does not change routing. Path param `id` (`floating_ip_id`). Body: form-encoded fields exposed by the Floating IP edit form (label/comment style). Returns the standard success-text response. Errors: 401 if unauthenticated; effectively 404 if `id` not owned by the caller. Read state first with `getFloatingIpInfo`.\n\nSibling ops: `getFloatingIpInfo` (read), `postFloatingIpsChangeIp` (re-route), `floating_ipsCancel`."
      },
      "delete": {
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/FloatingIpsCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "floating_ipsCancel",
        "summary": "Cancel a Floating IP service and release the IP — destructive, billing stops",
        "description": "Cancels the Floating IP via the shared `Api\\Billing\\CancelService` flow — flips status to canceled, halts recurring billing, and releases the IP back to the pool so it can no longer be re-routed. Not reversible: the customer cannot recover the same IP after release. Path param `id` (`floating_ip_id` from `getFloatingIpsList`). No body. Returns the `FloatingIpsCancelResponse` shape (success text / cancellation outcome). Errors: 401 if unauthenticated; 404 / cross-customer hidden when `id` is not owned by the caller; 409 if already canceled or otherwise non-cancelable. Confirm with the customer before calling — for routing changes use `postFloatingIpsChangeIp` instead of cancel-and-reorder.\n\nSibling ops: `getFloatingIpInfo` (status), `getFloatingIpInvoices` (outstanding charges), `postFloatingIpsChangeIp` (re-route instead of cancel), `addFloatingIp` (re-order)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The Floating IP service ID. Use the ID from `GET /floating_ips`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/floating_ips/{id}/change_ip": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/IpObject"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IpObject"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postFloatingIpsChangeIp",
        "summary": "Re-point a Floating IP to a different target IP on one of the customer's services",
        "description": "Reattaches the Floating IP by removing the old static route on the source switch and adding a new one on the destination switch (via `Sshwitch`), then updates `floating_ip_target_ip`. Use to move a portable IP between the customer's VPS / Quickservers / websites / dedicated servers without renumbering apps. Path param `id` (`floating_ip_id`). Body: `{ ip: <new target IP> }` (also accepts multipart form). Returns `{ success:true, text:'IP Changed' }`. Errors (returned via `json_error`): invalid IP format; IP not in our datacenter; IP not in use by an active service of this customer; service not active; another Floating IP already points to that target; switch lookup failures; route still present after removal. 401 if unauthenticated.\n\nSibling ops: `getFloatingIpInfo` (read current target), `getFloatingIpsList`, `floating_ipsCancel`. Read current target with `getFloatingIpInfo` first."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The Floating IP service ID. Use the ID from `GET /floating_ips`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/floating_ips/{id}/invoices": {
      "get": {
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getFloatingIpInvoices",
        "summary": "List all billing invoices charged against a specific Floating IP service",
        "description": "Use for a per-service billing history view — pulls the standard `Api\\Billing\\InvoicesList` rows scoped to this Floating IP. Read-only. Path param `id` (`floating_ip_id` from `getFloatingIpsList`). No body. Returns the `ChargeInvoiceRows` schema: array of invoice rows with id, date, amount, status, etc. Use the invoice IDs with the global billing endpoints (`getBillingInvoice`, `initiatePayment`) for line-item detail. Errors: 401 if unauthenticated; effectively 404 / cross-customer hidden when `id` is not owned by the caller. Siblings: `getFloatingIpInfo` (service details), `getFloatingIpsWelcomeEmail`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The Floating IP service ID. Use the ID from `GET /floating_ips`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/floating_ips/{id}/welcome_email": {
      "get": {
        "tags": [
          "Floating_IPs"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getFloatingIpsWelcomeEmail",
        "summary": "Resend the Floating IP welcome / setup email to the account contact",
        "description": "Triggers `floating_ip_welcome_email($id)` to re-deliver the original setup email (the IP, routing instructions, etc.) to the customer's on-file address. Useful when the email was lost or the customer needs the IP/setup details again. No body, no params besides path `id` (`floating_ip_id`). Returns `{ text: 'Welcome Email has been resent.' }`. Errors: 401 if unauthenticated; 404 (`Invalid Service Passed`) if `id` is not owned by the caller; 409 (`Service is not active`) if status is not `active`. Side effect: sends an outbound email — avoid in tight loops. Read state first via `getFloatingIpInfo` if unsure of status.\n\nSibling ops: `getFloatingIpInfo` (status), `addFloatingIp` (new order), `floating_ipsCancel`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The Floating IP service ID. Use the ID from `GET /floating_ips`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/home": {
      "get": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Home"
                }
              }
            },
            "description": "General information for use on the home page."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getHome",
        "summary": "Aggregate dashboard payload — service counts, recent activity, alerts",
        "description": "Use to render the post-login client portal home/dashboard. No body, no params. Returns the structure produced by `getClientHomeData()` — counts of active services per module (vps, webhosting, domains, mail, ssl, licenses, backups, floating_ips, scrub_ips, quickservers, servers), recent invoices, payment due alerts, ticket activity summaries, abuse/maintenance announcements, and account-level banners. Designed for one-shot dashboard hydration so individual modules don't each issue list calls. Cached implementation lives in `function_requirements('client_home')` -> `getClientHomeData()`. Errors: 401 if session is invalid or expired (unauthenticated). Sibling ops: `getSearch` (autocomplete), `getAccountInfo`, plus per-module list ops like `getVpsList`, `getDomainsList`, `getBillingInvoices`."
      }
    },
    "/search": {
      "get": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchAutocompleteResponse"
                }
              }
            },
            "description": "Search autocomplete results for the account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getSearch",
        "summary": "Global autocomplete across the caller's services, domains, and records",
        "description": "Use to power the global search box in the client portal — typeahead across services, domains, hostnames, IPs, and ticket subjects scoped to the current account (cross-account leakage is impossible). No body, no path params. Query string is conventionally `q=` though the underlying `getSearchAutoComplete($custid)` may match against multiple fields. Returns a `SearchAutocompleteResponse` object grouping hits by category (vps, domains, websites, mail, tickets, invoices, etc.) so the UI can render section headers. Optimized for low latency — does NOT replace per-module list ops for paginated browsing. Errors: 401 unauthenticated. Sibling ops: `getHome`, `getAccountInfo`, plus per-module list ops (`getVpsList`, `getDomainsList`, `getMailList`, `getTicketsList`)."
      }
    },
    "/info": {
      "get": {
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServicesInfo"
                }
              }
            },
            "description": "The modules and services information from the server."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "getInfo",
        "summary": "Discover available modules, service packages, categories, and types",
        "description": "Enumerates what services this MyAdmin install sells before placing orders or building a signup UI. Public — no auth required. Sibling ops: `getNewVps`, `getNewWebsite`, `getNewMail`, `getNewSsl`, `getNewLicense`, `getNewBackup`, `getNewQs`, `getNewServer` — each module's catalog op for buyable-package details.\n\n**Path/Query/Body:** None.\n\n**Returns:** `{ modules, services, serviceTypes, serviceCategories }`.\n- `modules` (array) — enabled plugin modules (`vps`, `webhosting`, `domains`, `ssl`, etc.).\n- `services` (object) — map of `services_id` → row from the `services` table, filtered to `services_buyable=1 AND services_hidden=0`, with `services_ourcost` / `services_hidden` stripped, and `services_id` / `services_category` / `services_type` cast to int and `services_cost` cast to float.\n- `serviceTypes` (object) — joins service rows to human-readable type names.\n- `serviceCategories` (object) — joins service rows to category names.\n\n**Auth:** None.\n\n**Errors:** No documented error path; 401 only if a stricter auth layer is added upstream.\n\n**Related calls:**\n- **Module-specific order catalog:** `getNewVps`, `getNewWebsite`, `getNewMail`, `getNewSsl`, `getNewLicense`, `getNewBackup`, `getNewQs`, `getNewServer`.\n- **Deeper health probe:** `pingServer`.\n"
      }
    },
    "/licenses": {
      "get": {
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LicenseRow"
                  }
                }
              }
            },
            "links": {
              "getLicenseInfo": {
                "operationId": "getLicenseInfo",
                "parameters": {
                  "id": "$response.body#/0/license_id"
                },
                "description": "Use the `license_id` from any item in the response array to fetch full service details via `GET /licenses/{id}`."
              }
            },
            "description": "The listing of `Licenses` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getLicenseList",
        "summary": "List all software licenses owned by the authenticated customer",
        "description": "Lists every software license service (cPanel, Plesk, LiteSpeed, CloudLinux, etc.) on the authenticated customer's account. Use this as the entry point for license management to discover the license_id needed by every other Licenses endpoint. Returns an array of rows including license_id, hostname, bound IP, services_name (license type), recurring cost, status (pending/active/canceled), and last invoice date/paid state. No path or query parameters; the customer scope is taken from the session. Errors: 401 when the session is missing or expired. Caveats: list is unpaginated, includes canceled rows so callers should filter by status. Sibling: getLicenseInfo for full details on one license."
      }
    },
    "/licenses/order": {
      "get": {
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicensesOrder"
                }
              }
            },
            "description": "License ordering information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewLicense",
        "summary": "Get available license types, packages, and pricing for ordering",
        "description": "Returns the catalog needed to build the license-order form: service categories (category_id->name), buyable service types (services_id, name, cost, billing frequency), package costs map keyed by services_id, the customer's currency symbol, and per-package field metadata via get_license_fields. Use this before addLicense to render type/package pickers and to validate a chosen package_id exists and is buyable (services_hidden=0, services_buyable=1). No path params or body. Returns LicensesOrder schema. Errors: 401 if unauthenticated. Sibling endpoints: putLicenses (validate selection), addLicense (place order). Note: pricing is converted to the session currency; coupon/IP/frequency are evaluated in the validate step, not here."
      },
      "put": {
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "description": "Validate Licenses order response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putLicenses",
        "summary": "Validate a software license order before placing it (dry run preview)",
        "description": "Dry-runs validate_buy_license against the same payload addLicense will accept, returning a structured result with continue=true/false plus errors[], normalized package, ip, service_cost, original_cost, coupon_code, custid, currency and service_extra. Always call this before addLicense to surface package/IP/coupon/TOS issues without creating an invoice. Body fields (form or JSON): package (services_id), ip, frequency (billing cycle months), coupon, comment, tos. No path params. Returns the validation object. Errors: 401 unauthenticated; 422-style errors are returned inside the body with continue=false rather than as HTTP errors. Caveat: a valid PUT does not reserve inventory; addLicense re-validates. Sibling: addLicense, getNewLicense."
      },
      "post": {
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a software license (cPanel, Plesk, etc.). Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addLicense",
        "summary": "Order a new software license and create the recurring invoice",
        "description": "Places an order for a new software license (cPanel, Plesk, LiteSpeed, etc.). Re-runs validate_buy_license then place_buy_license, which creates the repeat_invoices row, the first invoice, and queues payment processing. Always call putLicenses first to surface validation errors cheaply; addLicense re-validates and returns error JSON if continue=false. Body (form or JSON): package (services_id from getNewLicense), ip (target server IP the license binds to), frequency (billing months), coupon, comment, tos (truthy). No path params. Returns ServiceOrderPostResponse with the new service id and invoice info. Errors: 401 unauthenticated; validation or payment failures return json_error with the underlying message. Caveat: provisioning is asynchronous — poll getLicenseInfo for status.\n\nSibling ops: `getNewLicense` (catalog), `putLicenses` (validate), `getLicenseInfo` (poll status), `getLicenseInvoices`, `getBillingInvoice` + `initiatePayment` (settle invoice), `licensesCancel`."
      }
    },
    "/licenses/{id}": {
      "get": {
        "tags": [
          "Licenses"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The license service ID. Use `license_id` from `GET /licenses`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/License"
                }
              }
            },
            "links": {
              "postLicenseChangeIp": {
                "operationId": "postLicenseChangeIp",
                "parameters": {
                  "id": "$response.body#/serviceInfo/license_id"
                },
                "description": "Change the IP address assigned to this license."
              },
              "getLicenseInvoices": {
                "operationId": "getLicenseInvoices",
                "parameters": {
                  "id": "$response.body#/serviceInfo/license_id"
                },
                "description": "Retrieve the billing invoices for this license service."
              }
            },
            "description": "License information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getLicenseInfo",
        "summary": "Get full details for one license including status, IP, and links",
        "description": "Returns rich detail for a single license service: serviceInfo row (license_id, hostname, license_ip, license_status, license_type), the underlying services row (name, cost, frequency), client_links for self-service actions (change IP, cancel, resend welcome email, view invoices), and provisioning state. Use after getLicenseList to drill into a specific license, or as the canonical lookup before postLicenseChangeIp / licensesCancel / getLicenseInvoices. Path: id (license_id from list). No body. Errors: 401 unauthenticated; 404 if id is invalid or owned by a different customer. Caveat: admin_links/settings/csrf are stripped — use admin endpoints for those. Sibling endpoints: updateLicenseInfo (mutate fields), postLicenseChangeIp."
      },
      "post": {
        "tags": [
          "Licenses"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The license service ID. Use `license_id` from `GET /licenses`.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateLicenseInfo",
        "summary": "Update mutable fields on a license service (e.g. assigned IP)",
        "description": "Updates settings on an existing license service. The primary mutable field is the bound IP, but the endpoint shares routing with View::go so other future fields flow through here. For IP changes prefer postLicenseChangeIp which has explicit semantics and triggers vendor rebinding. Path: id (license_id). Body: fields to update (form or JSON); shape varies by license type. Returns SuccessTextResponse. Errors: 401 unauthenticated; 404 if id is invalid or not owned; 409 if license is not active. Caveats: vendor-side propagation (cPanel store, LiteSpeed key server, etc.) is asynchronous; some IP/hostname changes incur a fee per vendor policy. Sibling: getLicenseInfo (read), postLicenseChangeIp (dedicated)."
      },
      "delete": {
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/LicensesCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "licensesCancel",
        "summary": "Cancel a license service and stop future billing (irreversible)",
        "description": "Cancels a license service: invokes cancel_service which marks the service canceled, deactivates the license key with the upstream vendor, and stops the recurring invoice so no further charges occur. Use carefully — once vendor-side deactivation propagates the key stops working on the bound machine. Path: id (license_id from getLicenseList). No body. Returns LicensesCancelResponse with success and a translated text message. Errors: 401 unauthenticated; the underlying handler returns success=false JSON if the service id is invalid or cancellation fails (contact support path). Caveats: no prorated refund by default; pre-paid time is forfeited per TOS. Sibling endpoints: getLicenseInfo, getLicenseInvoices for billing history before cancelling."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The license service ID. Use `license_id` from `GET /licenses`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/licenses/{id}/change_ip": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IpObject"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/IpObject"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postLicenseChangeIp",
        "summary": "Rebind a license to a new IP address (may incur a vendor fee)",
        "description": "Changes the IP address that the license is bound to and triggers re-issuance with the upstream vendor (cPanel store, LiteSpeed key server, Plesk, etc.). The service must be active. Use getLicenseInfo first to read the current license_ip, then submit the new IP. Path: id (license_id). Body (JSON or multipart): IpObject with the new ip field. Returns SuccessTextResponse on success. Errors: 401 unauthenticated; 404 invalid id or not owned; 409 if status != active; 422-style failures from the vendor are returned via json_error with the upstream status_text. Caveats: many vendors charge a per-change fee and rate-limit changes (e.g. cPanel allows limited free changes per period); the new IP must be reachable for license verification. Sibling: updateLicenseInfo."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The license service ID. Use `license_id` from `GET /licenses`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/licenses/{id}/invoices": {
      "get": {
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getLicenseInvoices",
        "summary": "List all billing invoices tied to one software license service",
        "description": "Returns the full invoice history for a single license service: the original setup invoice plus every recurring renewal invoice generated by the repeat_invoices entry. Use this for billing reconciliation, to display past charges in the customer UI, or to confirm a renewal posted before contacting support. Path: id (license_id from getLicenseList). No body. Returns ChargeInvoiceRows: an array of invoice rows with id, date, amount, paid status, and payment method. Errors: 401 unauthenticated; returns success=false with HTTP 400 if the service id is invalid or owned by a different customer. Caveat: only invoices linked via repeat_invoices_id are included — manual one-off charges from staff may not appear here. Sibling endpoints: getLicenseInfo, licensesCancel."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The license service ID. Use `license_id` from `GET /licenses`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/licenses/{id}/welcome_email": {
      "get": {
        "tags": [
          "Licenses"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getLicensesWelcomeEmail",
        "summary": "Resend the license welcome email with the key and activation steps",
        "description": "Resends the welcome email for an active license to the account email on file. The email contains the license key, the bound IP, and vendor-specific activation instructions (e.g. cPanel /usr/local/cpanel/cpkeyclt, LiteSpeed lswsctrl). Use this when the customer lost the original email or rotated mailboxes — the key itself is unchanged. Path: id (license_id). No body. Returns SuccessTextResponse with a translated confirmation. Errors: 401 unauthenticated; 404 if the id is invalid or not owned by the session customer; 409 if the license status is not active (cancelled licenses cannot resend). Caveat: delivery is best-effort — check the email log if it does not arrive. Sibling endpoints: getLicenseInfo, postLicenseChangeIp."
      },
      "parameters": [
        {
          "name": "id",
          "description": "The license service ID. Use `license_id` from `GET /licenses`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/oauth": {
      "summary": "OAuth Authentication",
      "description": "Authenticate using third-party OAuth providers such as Google. Use GET to initiate the OAuth redirect, POST to handle the callback, and PATCH to complete two-factor verification if required.",
      "get": {
        "tags": [
          "Public"
        ],
        "parameters": [
          {
            "name": "provider",
            "description": "The OAuth provider name (e.g. `Google`).",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "redirect_url": {
                      "description": "The URL to redirect the user to for OAuth authentication.",
                      "type": "string"
                    }
                  }
                }
              }
            },
            "links": {
              "postOauthCallback": {
                "operationId": "postOauthCallback",
                "description": "After the user completes OAuth authorization at the redirect URL, call `POST /oauth` with the provider to handle the callback."
              }
            },
            "description": "OAuth redirect URL for the requested provider."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "getOauthRedirect",
        "summary": "Begin OAuth login flow — redirect user to provider for authentication",
        "description": "Use as step 1 of social login. Navigate the browser (typically a popup) to `/apiv2/oauth?provider=X` so the provider authenticates the user, then handle the postMessage from the popup. Public — no auth required.\nQuery params: `provider` (required, case-sensitive: `Google`/`GitHub`/`Facebook`/`Twitter`), `origin` (optional, opener window origin used to target postMessage instead of `*`).\nThe endpoint redirects directly to the provider rather than returning JSON. After the provider callback, the popup posts one of: `oauth_success` (logged in), `oauth_2fa_required` (call `patchOauthTwoFactor` with the `oauth_token`), `oauth_link_required` (call `postOauthCallback` to link or create), or `oauth_error`.\nSiblings: `postOauthCallback`, `patchOauthTwoFactor`, `submitLogin` (password flow)."
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "provider": {
                    "description": "The OAuth provider name (e.g. `Google`).",
                    "type": "string"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "provider": {
                    "description": "The OAuth provider name (e.g. `Google`).",
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Public"
        ],
        "parameters": [
          {
            "name": "provider",
            "description": "The OAuth provider name (e.g. `Google`).",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "login": {
                      "description": "Whether the user was logged in to an existing account.",
                      "type": "boolean"
                    },
                    "signup": {
                      "description": "Whether a new account was created.",
                      "type": "boolean"
                    },
                    "linked": {
                      "description": "Whether the OAuth provider was linked to an existing account.",
                      "type": "boolean"
                    },
                    "account_id": {
                      "description": "The account ID associated with the OAuth login.",
                      "type": "integer"
                    },
                    "error_code": {
                      "description": "Error code if additional verification is needed (e.g. `2fa_required`).",
                      "type": "string"
                    }
                  }
                }
              }
            },
            "links": {
              "patchOauthTwoFactor": {
                "operationId": "patchOauthTwoFactor",
                "parameters": {
                  "account_id": "$response.body#/account_id"
                },
                "description": "When the response contains `error_code: \"2fa_required\"`, use the returned `account_id` with `PATCH /oauth` to complete two-factor verification."
              }
            },
            "description": "OAuth callback result."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "postOauthCallback",
        "summary": "Complete OAuth login by linking provider to existing or new account",
        "description": "Step 3 of the OAuth login flow. Called after `getOauthRedirect` returned `oauth_link_required` via the popup's `window.postMessage()`. Either links the OAuth identity to an existing account (verifying password) or creates a new account. Public — no auth required. Sibling ops: `patchOauthTwoFactor` (2FA follow-up), `getOauthRedirect` (start the flow), `submitSignup`, `submitLogin`.\n\n**Body fields** (JSON or form):\n- `oauth_token` (string, required) — signed token from the popup's `window.postMessage()` payload; 10-minute expiry.\n- `login` (string, required) — email.\n- `password` (string, required).\n- `create` (boolean, optional) — set `true` to create a new account instead of linking.\n- `email_confirmation` (string, conditional) — 8-char code emailed on the first `create=true` attempt; server returns 422 `email_verification_required` until provided.\n- `tfa` (string, conditional) — 6-digit TOTP when the existing account has 2FA enabled (after the first attempt returns 422 `2fa_required`).\n\n**Returns:** `{ login|signup|linked: true, sessionId, account_id, account_lid, ima, name, gravatar }`.\n\n**Errors:**\n- `400` — invalid / expired `oauth_token`.\n- `401` — bad password or wrong 2FA code.\n- `409` — account already exists (when `create: true`).\n- `422` — missing field; `email_verification_required`; `2fa_required`.\n\n**Related calls:**\n- **Prerequisite:** `getOauthRedirect` to initiate the popup flow.\n- **Follow-up when 2FA required:** `patchOauthTwoFactor`.\n- **Alternate entry points:** `submitLogin`, `submitSignup`.\n"
      },
      "patch": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "account_id",
                  "code"
                ],
                "type": "object",
                "properties": {
                  "account_id": {
                    "description": "The account ID returned from the POST callback.",
                    "type": "integer"
                  },
                  "code": {
                    "description": "The 6-digit two-factor authentication code.",
                    "type": "string"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "required": [
                  "account_id",
                  "code"
                ],
                "type": "object",
                "properties": {
                  "account_id": {
                    "description": "The account ID returned from the POST callback.",
                    "type": "integer"
                  },
                  "code": {
                    "description": "The 6-digit two-factor authentication code.",
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "login": {
                      "description": "Whether the 2FA verification succeeded and the user is now logged in.",
                      "type": "boolean"
                    }
                  }
                }
              }
            },
            "description": "OAuth 2FA verification result."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "patchOauthTwoFactor",
        "summary": "Submit 2FA code to finish OAuth login when account has 2FA enabled",
        "description": "Final step of the OAuth login flow when the account has 2FA enabled. Called after `postOauthCallback` (or the popup's `window.postMessage()` handshake) returned `2fa_required`. Verifies the TOTP against the account's stored Google Authenticator secret and creates the session. Public — no auth required. Sibling ops: `postOauthCallback` (prior step), `getOauthRedirect` (entry point), `getAccountTfaSetup` (enroll 2FA), `submitLogin`.\n\n**Body fields** (JSON or form):\n- `code` (string, required) — 6-digit TOTP from the authenticator app.\n- `account_id` (integer, required) — returned by the prior `postOauthCallback`.\n- `oauth_token` (string, optional) — signed token from the original `postMessage` payload, type `2fa`, 10-minute expiry. When present, its embedded OAuth profile data is merged into the account (name / picture / phone / address) for any fields still empty.\n\n**Returns:** `{ login: true, sessionId, account_id, account_lid, ima, name, gravatar }`.\n\n**Errors:**\n- `400` — invalid / expired `oauth_token` or no pending verification.\n- `401` — invalid 2FA code.\n- `409` — 2FA not enabled on the account.\n- `422` — missing `code`.\n\n**Related calls:**\n- **Prerequisite:** `postOauthCallback`.\n- **Enroll 2FA on the account first:** `getAccountTfaSetup` → `updateAccountTfa`.\n- **Alternate login:** `submitLogin`.\n"
      }
    },
    "/login": {
      "summary": "Log In to the system and create a session.",
      "description": "Submit your login credentials to generate a session id which can be used for validating requests.",
      "get": {
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginInfo"
                },
                "examples": {
                  "LoginInfoExample": {
                    "value": {
                      "logo": "//my.interserver.net/images/logos/mystaging.png",
                      "captcha": "data:image/jpeg;base64,/9j/",
                      "language": "en-US",
                      "counts": {
                        "vps": 290201,
                        "websites": 205172,
                        "servers": 27940
                      }
                    }
                  }
                }
              }
            },
            "description": "Various pieces of information useful for rendering a login page."
          },
          "403": {
            "$ref": "#/components/responses/LoginResponseError"
          }
        },
        "security": [
          {}
        ],
        "operationId": "getLoginInfo",
        "summary": "Fetch logo, captcha, language, and stats for rendering a login page",
        "description": "Bootstraps an unauthenticated login page in one round-trip — branding logo, fresh captcha challenge, auto-detected user language, and live counts of VPS / websites / servers managed by the system (often used as marketing stats). Public — no auth required. Sibling ops: `submitLogin` (consume the captcha), `getCaptcha` (refresh captcha only), `getAccountLocales`, `submitSignup`.\n\n**Path/Query/Body:** None.\n\n**Returns** `{ logo, captcha, language, counts }`:\n- `logo` (string) — URL; uses the `LOGO` constant or a default.\n- `captcha` (string) — `data:image/jpeg;base64,...` image; phrase is stored server-side under `$_SESSION['captcha']` (also aliased to `$_SESSION['captchaSignup']` and `$_SESSION['captchaFP']`) — the browser's `PHPSESSID` cookie carries the phrase to `submitLogin` / `submitSignup`.\n- `language` (string) — BCP-47 locale (e.g. `en-US`).\n- `counts` (object) — `{ vps: int, websites: int, servers: int }` from live `SELECT COUNT(*)` on the underlying tables.\n\n**Auth:** None.\n\n**Errors:** `403` per `LoginResponseError` if a stricter login gate is configured upstream.\n\n**Related calls:**\n- **Next:** `submitLogin` (login form post) or `submitSignup` (new account).\n- **Captcha refresh only:** `getCaptcha`.\n- **OAuth alternative:** `getOauthRedirect`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/LoginSubmissionExample"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginSubmissionExample"
              },
              "examples": {
                "LoginSubmissionExampleExample": {
                  "value": {
                    "login": "user@domain.com",
                    "passwd": "mypassword",
                    "remember": "true",
                    "g-recaptcha-response": {
                      "__v_isShallow": false,
                      "dep": {
                        "w": 0,
                        "n": 0
                      },
                      "__v_isRef": true,
                      "_rawValue": "zzzzz",
                      "_value": "zzzzz"
                    }
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/LoginSuccessResponse"
          },
          "402": {
            "$ref": "#/components/responses/LoginResponseError"
          },
          "default": {
            "description": "Default response"
          }
        },
        "security": [
          {}
        ],
        "operationId": "submitLogin",
        "summary": "Authenticate with email + password and return a session token",
        "description": "Primary password→session-token exchange. Pass the returned session id back as the `sessionid` HTTP header on subsequent calls. Public — no auth required. Sibling ops: `getLoginInfo` (captcha + branding), `getOauthRedirect` (social login), `submitSignup`, `updateAccountApiKey` (rotate API key once logged in).\n\n**Body fields** (JSON or form):\n- `login` (string, required) — email.\n- `passwd` (string, required) — password.\n- `tfa` (string, conditional) — 6-digit TOTP when the account has 2FA enabled.\n- `verify` (string, conditional) — 8-char email-confirmation code returned via email when logging in from a new IP. Triggered automatically when the IP has no `acquittal` trial record yet (see `Trial` ORM, type `verify_email`).\n- `remember` (boolean / `'true'` / `'yes'` / `'1'`, optional) — extends cookie lifetime.\n\n**Returns:** `{ sessionId, account_id, account_lid, ima, name, gravatar }`. The `sessionId` value is the credential to send on every subsequent authenticated request.\n\n**Errors:**\n- `401` — bad credentials or wrong 2FA / verify code.\n- `422` — missing `login` / `passwd` / `tfa` / `verify`; response body's `field` indicates which input is required next.\n- `429` — too many failed attempts (login-log rate-limit) or max code retries reached.\n\n**Related calls:**\n- **Prerequisite:** `getLoginInfo` to fetch the captcha challenge and counts.\n- **Alternate:** `getOauthRedirect` → `postOauthCallback` for social login.\n- **After login:** `updateAccountApiKey`.\n"
      }
    },
    "/logout": {
      "get": {
        "tags": [
          "Account"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "Logout",
        "summary": "Destroy the current API/web session — token becomes unusable",
        "description": "DESTRUCTIVE: invalidates the caller's session record and appsession bag. After this returns the session id can no longer authenticate requests; the client must discard it and prompt the user to log in again. Idempotent — calling with an already-invalid session returns `200` (no-op when `App::accounts()->data` is empty). API keys (`updateAccountApiKey`) and persistent OAuth links are NOT affected — only this session token. Sibling ops: `updateAccountPassword`, `updateAccountApiKey`, `logoutAccountOauth`, `deleteAccountOauthName`.\n\n**Path/Query/Body:** None.\n\n**Returns:** `{ success: true, text: 'Logged Out' }`.\n\n**Side effects:** calls `App::session()->destroy()` only when `api_check_auth_limits()` passes for the current account, so a locked account is short-circuited gracefully without further error.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — only on a completely malformed auth header.\n\n**Related calls:**\n- **Re-login:** `submitLogin` or `getOauthRedirect`.\n- **Per-provider OAuth sign-out (does NOT invalidate the session):** `logoutAccountOauth`.\n"
      }
    },
    "/mail": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MailRow"
                  }
                }
              }
            },
            "links": {
              "getMailInfo": {
                "operationId": "getMailInfo",
                "parameters": {
                  "id": "$response.body#/0/mail_id"
                },
                "description": "Use the `mail_id` from any item in the response array to fetch full service details via `GET /mail/{id}`."
              }
            },
            "description": "The listing of `Mail` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailList",
        "summary": "List every Mail Baby SMTP relay service on the account",
        "description": "Enumerates every Mail Baby SMTP relay service owned by the authenticated customer. Canonical entry point for finding a `mail_id` to pass to other Mail endpoints. Filtered server-side by `mail_custid`. Sibling ops: `getMailInfo`, `getStats`, `viewMailLog`, `getMailDeliverability`, `getMailBlocks`, `getMailInvoices`, `addMail`.\n\n**Path/Query/Body:** None.\n\n**Returns:** Array of `MailRow`:\n- `mail_id` (integer) — canonical id.\n- `mail_username` (string) — SMTP username (e.g. `mb1234`).\n- `mail_status` (string enum) — `active` / `pending` / `canceled` / `suspended`.\n- `services_name` (string) — plan label.\n- `repeat_invoices_cost` (decimal string) — recurring cost.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Per-service detail:** `getMailInfo`.\n- **Send mail:** `sendMail` / `sendAdvMail`.\n- **Reputation:** `getMailDeliverability` / `getMailBlocks` / `getMailDelist`.\n- **Order a new service:** `getNewMail` → `putMail` → `addMail`.\n"
      }
    },
    "/mail/order": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailOrder"
                }
              }
            },
            "description": "Mail ordering information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewMail",
        "summary": "Read the Mail Baby order catalog — plans, package costs, service-type metadata",
        "description": "Step 1 of the Mail Baby order flow. Returns the catalog used to bootstrap an order form: `packageCosts` keyed by `services_id` (only buyable services where `services_buyable=1`) and the full `serviceTypes` map. Read-only. Pricing is normalized to the customer's currency via `getCurrency()`. Sibling ops: `putMail`, `addMail`, `getMailList`.\n\n**Path/Query/Body:** None.\n\n**Returns** (schema `MailOrder`):\n- `packageCosts` (object) — `{<services_id>: <cost>}` per buyable plan.\n- `serviceTypes` (object) — full service-types registry (plan metadata).\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Next:** `putMail` (validate + quote — no charge), `addMail` (place order).\n"
      },
      "put": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "description": "Validate Mail order response."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putMail",
        "summary": "Validate Mail Baby order, quote pricing, and verify coupon — no charge",
        "description": "Step 2 of the Mail Baby order flow. Dry-runs the order through `validate_buy_mail()` without creating invoices. Returns the cost preview, coupon resolution, and validation errors. The endpoint also auto-generates an SMTP password preview the order will use. Use to surface live pricing in the UI before `addMail`. Sibling ops: `getNewMail`, `addMail`.\n\n**Body fields:**\n- `serviceType` (integer, required) — plan id from `getNewMail.packageCosts` keys.\n- `coupon` (string, optional) — coupon code.\n\n**Returns:**\n- `continue` (bool) — `true` if order can safely be POSTed.\n- `errors` (array) — validation messages.\n- `serviceType`, `serviceCost`, `originalCost`, `repeatServiceCost` (numeric).\n- `password` (string) — auto-generated SMTP password preview.\n- `introFrequency` (integer).\n- `coupon`, `couponCode` (string/integer) — resolved coupon.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `200` with `continue=false` and `errors[]` — validation problems.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Prerequisite:** `getNewMail` (catalog).\n- **Place order:** `addMail`.\n"
      },
      "post": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a mail.baby relay/transactional mail service. Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addMail",
        "summary": "Place a new Mail Baby order, generate invoice, and queue provisioning",
        "description": "Step 3 of the Mail Baby order flow. Revalidates via `validate_buy_mail()`, then calls `place_buy_mail()` to create a `Repeat_Invoice` recurring billing row, an initial `invoices` row, and a `mail` service record in pending status. SMTP credentials become active once the activation worker runs the welcome email (after the invoice is paid). **Real money** — call `putMail` first. Sibling ops: `getNewMail`, `putMail`, `getMailInfo`, `initiatePayment`.\n\n**Body fields:**\n- `serviceType` (integer, required) — plan id from `getNewMail`.\n- `coupon` (string, optional).\n- `comment` (string, optional) — saved on the order row.\n\n**Returns** (on success): `{continue: true, total_cost, iid, iids, real_iids, serviceId (new mail_id), invoice_description, cj_params}` — pass `real_iids` to `initiatePayment`. On validation failure: `{continue: false, errors: [...]}` with HTTP 200.\n\n**Side effects:**\n- Inserts `mail` service row in `pending` status.\n- Inserts `repeat_invoices` + `invoices` rows.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Pay:** `initiatePayment` with `real_iids`.\n- **Confirm activation:** `getMailInfo` (poll until `mail_status=='active'`).\n- **Resend credentials:** `getMailWelcomeEmail`.\n\n**Full ordering happy path:**\n```text\nGET /mail/order                                    -> catalog (getNewMail)\nPUT /mail/order { serviceType, coupon? }           -> quote (putMail)\nPOST /mail/order { serviceType, coupon?, comment? } -> { serviceId, real_iids }\nGET /billing/pay/cc/{real_iids[0]}                 -> pay (initiatePayment)\nGET /mail/{serviceId}                              -> poll until mail_status=='active'\n```\n"
      }
    },
    "/mail/{id}": {
      "get": {
        "tags": [
          "Mail"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailSchema"
                }
              }
            },
            "links": {
              "getMailBlocks": {
                "operationId": "getMailBlocks",
                "parameters": {
                  "id": "$response.body#/serviceInfo/mail_id"
                },
                "description": "Retrieve blocked email addresses for this mail service."
              },
              "getMailAlerts": {
                "operationId": "getMailAlerts",
                "parameters": {
                  "id": "$response.body#/serviceInfo/mail_id"
                },
                "description": "Retrieve the alert configuration for this mail service."
              },
              "getMailInvoices": {
                "operationId": "getMailInvoices",
                "parameters": {
                  "id": "$response.body#/serviceInfo/mail_id"
                },
                "description": "Retrieve the billing invoices for this mail service."
              }
            },
            "description": "Mail Information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailInfo",
        "summary": "Read full detail for one Mail Baby service including SMTP credentials",
        "description": "Returns the full `ViewMail` payload for one Mail Baby service — `serviceInfo`, `serviceType`, and `client_links` (URLs rewritten to API paths, e.g. `view_mail_log` → `log`). Admin fields (`admin_links`, `settings`, `csrf`) stripped. Use to render a service dashboard or retrieve SMTP host/username for MTA configuration. Sibling ops: `getMailList`, `updateMailInfo`, `mailCancel`, `resetMailPassword`, `getMailWelcomeEmail`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns** (schema `MailSchema`):\n- `serviceInfo` — `mail_id`, `mail_username` (e.g. `mb1234`), `mail_status`, `mail_invoice`, `mail_custid`, dates, currency.\n- `serviceType` — plan row (`services_ourcost` stripped).\n- `client_links` (array) — action URLs (log, alerts, blocks, etc.).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n\n**Related calls:**\n- **Send:** `sendMail` / `sendAdvMail`.\n- **Rotate password:** `resetMailPassword`.\n- **Reset credentials:** `getMailWelcomeEmail`.\n- **Cancel:** `mailCancel`.\n"
      },
      "post": {
        "tags": [
          "Mail"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateMailInfo",
        "summary": "POST mutation hook for the Mail Baby service detail page",
        "description": "POST mutation hook for the Mail Baby service detail page. Currently delegates to the same `View::go()` handler as `getMailInfo` — placeholder for future field updates. Does NOT rotate credentials (use `resetMailPassword`) and does NOT change billing (use `/billing` endpoints). Sibling ops: `getMailInfo`, `mailCancel`, `resetMailPassword`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body:** Form fields.\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `mail_status != \"active\"`.\n\n**Related calls:**\n- **Read:** `getMailInfo`.\n- **Rotate password:** `resetMailPassword`.\n"
      },
      "delete": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/MailCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "mailCancel",
        "summary": "Cancel a Mail Baby service and stop the recurring invoice",
        "description": "Cancels the Mail Baby service through the shared `Billing\\CancelService::go($id)` flow with `module='mail'`. SMTP credentials are deactivated, the service transitions to canceled, the `repeat_invoice` is stopped, and queued submissions stop being accepted. **Irreversible via API** — re-activation requires placing a new order via `addMail`. Sibling ops: `getMailInfo`, `getMailInvoices`, `addMail`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns:** `MailCancelResponse`.\n\n**Side effects:**\n- Sets `mail_status='canceled'`.\n- Marks `repeat_invoices` non-renewing.\n- ZoneMTA-side: stops accepting new submissions for `mb{mail_id}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n\n**Related calls:**\n- **Sibling cancels:** `VPSCancel`, `CancelDomain`, `webhostingCancel`, etc.\n- **Re-provision:** `addMail`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/blocks": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailBlocks"
                },
                "examples": {
                  "MailBlocksExample": {
                    "value": {
                      "local": [
                        {
                          "date": "2023-08-07",
                          "from": "user@domain.com",
                          "messageId": "pFaRqFUEWkucjhTuIzYuoAgWU@domain.com",
                          "subject": "Test Email",
                          "to": "['client@site.com']"
                        }
                      ],
                      "mbtrap": [
                        {
                          "date": "2023-08-07",
                          "from": "user@domain.com",
                          "messageId": "pFaRqFUEWkucjhTuIzYuoAgWU@domain.com",
                          "subject": "Test Email",
                          "to": "['client@site.com']"
                        }
                      ],
                      "subject": [
                        {
                          "from": "user@domain.com",
                          "subject": "Test Email"
                        }
                      ]
                    }
                  }
                }
              }
            },
            "links": {
              "delistBlockLink": {
                "operationId": "delistBlock",
                "parameters": {
                  "email": "$response.body#/local/0/from"
                },
                "description": "The `from` value returned in the response can be used as the `email` parameter in `DELETE /mail/{id}/blocks`."
              }
            },
            "description": "OK"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailBlocks",
        "summary": "List recent local-blocklist hits and spam-trap captures for the mail user",
        "description": "Returns relay-side block events for the SMTP user behind `mail_id` — the last 24 hours of `LOCAL_BL_RCPT` and `MBTRAP` rspamd hits, plus a 3-day window of suspicious-subject hits (credential-leak heuristic firing on subjects containing `@` / `smtp` / `socks5` / `socks4` more than 4 times). Use the `from` value with `delistBlock` or `postMailDelist` to clear a block. Sibling ops: `delistBlock`, `getMailDelist`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns** (schema `MailBlocks`):\n- `local` (array) — rspamd `LOCAL_BL_RCPT` hits: `{date, from, messageId, subject, to}`.\n- `mbtrap` (array) — spam-trap captures (`MBTRAP` symbol): same shape.\n- `subject` (array) — senders flagged by subject-line heuristic: `{from, subject}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `mail_status != \"active\"`.\n\n**Related calls:**\n- **Clear a block:** `delistBlock` (POST `/mail/{id}/blocks/delete`).\n- **Broader delist UI:** `getMailDelist`, `postMailDelist`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/alerts": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailAlertsResponse"
                }
              }
            },
            "description": "Alert configuration for the mail service."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailAlerts",
        "summary": "List configured delivery/bounce/quota alerts for one Mail Baby service",
        "description": "Returns every alert row from `alerts` matching this service. Each row carries `alert_id` (use with PUT/DELETE), `alert_type`, `alert_value` (threshold), `alert_to` (notification email), `alert_enabled`, and timestamps. Sibling ops: `createMailAlert`, `updateMailAlert`, `deleteMailAlert`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns** (schema `MailAlertsResponse`): array of alert rows.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `401`, `404`, `409 not active`.\n"
      },
      "put": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailAlertUpdateRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/MailAlertUpdateRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateMailAlert",
        "summary": "Update an existing Mail Baby alert by alert_id",
        "description": "Updates a single alert row by `alert_id`. Handler verifies the alert belongs to this service+module before writing. Sibling ops: `getMailAlerts`, `createMailAlert`, `deleteMailAlert`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields (schema `MailAlertUpdateRequest`):**\n- `alert_id` (integer, required) — from `getMailAlerts`.\n- `type` (string, required).\n- `value` (string/numeric, required) — threshold.\n- `to` (string, required) — notification email; validated via `FILTER_VALIDATE_EMAIL`.\n- `enabled` (bool, optional).\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `Invalid alert!` (alert not owned), field-level errors for missing/invalid body, `401`, `404`, `409 not active`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailAlertRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/MailAlertRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "createMailAlert",
        "summary": "Create a new Mail Baby alert for delivery, bounce, or quota events",
        "description": "Inserts a new alert row via the `Alert` ORM. The new `alert_id` is retrievable via `getMailAlerts`. Sibling ops: `getMailAlerts`, `updateMailAlert`, `deleteMailAlert`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields (schema `MailAlertRequest`):**\n- `type` (string, required).\n- `value` (string/numeric, required) — threshold.\n- `to` (string, required) — notification email; validated via `FILTER_VALIDATE_EMAIL`.\n- `enabled` (bool, optional).\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** field-level errors for missing/invalid body, `401`, `404`, `409 not active`.\n"
      },
      "delete": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "alert_id"
                ],
                "properties": {
                  "alert_id": {
                    "description": "The ID of the alert to delete.",
                    "type": "integer"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "alert_id"
                ],
                "properties": {
                  "alert_id": {
                    "description": "The ID of the alert to delete.",
                    "type": "integer"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteMailAlert",
        "summary": "Delete a Mail Baby alert by alert_id (hard delete — no recovery)",
        "description": "Hard-deletes a single alert row. Handler verifies the alert belongs to this service+module before deleting. **Irreversible** — no history is preserved; recreate via `createMailAlert` if needed. Sibling ops: `getMailAlerts`, `createMailAlert`, `updateMailAlert`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields:**\n- `alert_id` (integer, required) — from `getMailAlerts`.\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `Invalid alert!` (alert not owned), `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/delist": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailDelistResponse"
                }
              }
            },
            "description": "Blocklist entries and delist details for the mail service."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailDelist",
        "summary": "Read blocklist diagnostics and find senders eligible for delisting",
        "description": "Returns a richer diagnostic snapshot than `getMailBlocks` — intended for the delist UI. Use any `SMTPFrom`/`from` value as the `unblock` field for `postMailDelist`. Sibling ops: `postMailDelist`, `getMailBlocks`, `delistBlock`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns** (schema `MailDelistResponse`):\n- `id` (integer) — `mail_id` echo.\n- `local`, `mbtrap` (array) — last 24h rspamd hits with capitalized keys (`Date`, `SMTPFrom`, `MessageId`, `Subject`, `MimeRecipients`).\n- `subject` (array) — credential-leak-heuristic firings (3-day window).\n- `manual` (array) — manually added blocks.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `401`, `404`, `409 not active`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MailDelistRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/MailDelistRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postMailDelist",
        "summary": "Delist a sender from rspamd / mailchannels / mailbaby block lists",
        "description": "Removes all block rows for one sender email across three reputation stores: `rspamd` (by `fromemail`), `mailchannels` (by `email`), `mailbaby` (by `emailfrom`). Effect is global per-address across all three tables; takes effect immediately for new submissions. Sibling ops: `getMailDelist`, `delistBlock` (alias at `/mail/{id}/blocks/delete`), `getMailBlocks`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields (schema `MailDelistRequest`):**\n- `unblock` (string, required) — sender email from `getMailDelist`/`getMailBlocks`.\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `Missing parameter unblock`, `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/deliverability": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailDeliverabilityResponse"
                }
              }
            },
            "description": "Deliverability metrics for the mail service."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailDeliverability",
        "summary": "Read delivered vs bounced totals broken down by sender (or by recipient domain)",
        "description": "Returns deliverability analytics from `MailDeliveryStats` (Dragonfly cache) for the SMTP user behind `mail_id`. Default pivot is by sender; pass `?filter_domain=1` to pivot by recipient domain for the current year instead. Use to drive analytics dashboards. Sibling ops: `getStats`, `viewMailLog`, `getMailBlocks`, `getMailDelist`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Query params:**\n- `filter_domain` (string `1`, optional) — pivot by recipient domain instead of sender.\n\n**Returns** (schema `MailDeliverabilityResponse`):\n- `stat`: `{delivered, bounced, percent}` — totals and bounce ratio.\n- `header` (string), `col1` (string) — table headers.\n- `table_data` (array) — rows of `[<sender-or-domain>, bounced, delivered, bouncePercent]`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/invoices": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailInvoices",
        "summary": "List billing invoices linked to this Mail Baby service",
        "description": "Returns every invoice associated with this `mail_id` via the shared `InvoicesList` workflow. Use to render per-service billing history or find unpaid invoices to pay via `initiatePayment`. Sibling ops: `getBillingInvoice`, `initiatePayment`, `addMail`, `mailCancel`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns:** `ChargeInvoiceRows` — array of `{id, amount, currency, paid, date, due_date, description, module: \"mail\", service}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `401`, `404 Invalid Service`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/reset_password": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "resetMailPassword",
        "summary": "Rotate the SMTP password and email the new credential to the account owner",
        "description": "Generates a new 20-char SMTP password (lower/upper/digits via `generate_password`), writes it to the ZoneMTA Mongo `users` collection for username `mb{mail_id}`, logs the change to `App::history()`, and emails the result to the account-on-file via `client_email.tpl`. **Any MTA, app, or saved client still using the old password will start failing auth immediately.** The new password is **not** returned in the response — fetch via `getMailWelcomeEmail` or `getMailInfo`. Sibling ops: `getMailWelcomeEmail`, `getMailInfo`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns:** `SuccessTextResponse`.\n\n**Side effects:**\n- Mongo update on ZoneMTA `users` for `mb{mail_id}`.\n- `App::history()` audit entry.\n- Email sent to account owner.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** Mongo update modified 0 rows → error text; `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/welcome_email": {
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getMailWelcomeEmail",
        "summary": "Resend the Mail Baby welcome email with SMTP credentials and setup info",
        "description": "Re-runs the `mail_welcome_email` plugin function — composes and sends the standard welcome email (SMTP host `relay.mailbaby.net`, port, username `mb{mail_id}`, current password, configuration tips) to the account-on-file. Use after `resetMailPassword` to redeliver the rotated credential, or when a customer reports losing the original setup email. Idempotent. Sibling ops: `resetMailPassword`, `getMailInfo`. Cross-module welcome-email endpoints: `getVpsWelcomeEmail`, `getWebsitesWelcomeEmail`, `getDomainsWelcomeEmail`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns:** `{text: \"Welcome Email has been resent.\"}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/billing/pay/{method}/{invoices}": {
      "get": {
        "tags": [
          "Billing"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "description": "The response type indicating how to handle the payment. Possible values: `redirect` (redirect user to a URL), `submit` (submit a form to a URL), `single` (immediate result).",
                      "enum": [
                        "redirect",
                        "submit",
                        "single"
                      ],
                      "type": "string"
                    },
                    "redirect": {
                      "description": "URL to redirect the user to for payment (when type is `redirect`).",
                      "type": "string"
                    },
                    "action": {
                      "description": "Form action URL (when type is `submit`).",
                      "type": "string"
                    },
                    "method": {
                      "description": "HTTP method for the form submission (when type is `submit`).",
                      "type": "string"
                    },
                    "items": {
                      "description": "Form field name-value pairs to submit (when type is `submit`).",
                      "type": "object"
                    },
                    "text": {
                      "description": "Status or result text.",
                      "type": "string"
                    }
                  }
                }
              }
            },
            "description": "Payment initiation response with redirect or form data."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "charge",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Pay one or more invoices using the chosen method (cc, paypal, btcpay, coinbase, payu, ccavenue, cashfree, payssion, prepay).",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "initiatePayment",
        "summary": "Pay invoices through the chosen gateway — returns the next-step action",
        "description": "Universal payment trigger — the final step in every order/checkout flow. Use after any order endpoint (`addVps`, `addQs`, `addBackup`, `addMail`, `addBillingPrepay`) returns an invoice id, or after `getBillingInvoices` surfaces unpaid invoices. Resolves the chosen gateway class under `include/Api/Billing/Pay/`, populates it with the invoices, and returns one of three response shapes the client must act on: `redirect` (send the user to the gateway URL), `submit` (POST a form with the supplied items), or `single` (processed synchronously). Sibling ops: `getBillingCart`, `getBillingInvoices`, `getBillingInvoice`, `addBillingPrepay`, `updateBillingPaymentMethod`, `addBillingCreditCard`.\n\n**Path params:**\n- `method` (string enum, required) — one of `cc`, `paypal`, `prepay`, `payssion`, `payu`, `ccavenue`, `cashfree`, `coinbase`, `btcpay`. Rejected with 400 otherwise.\n- `invoices` (string, required) — comma-separated identifiers. Each identifier may be:\n  - a bare integer invoice id (e.g. `25296600`);\n  - `INV<module><iid>` (e.g. `INVvps25296600`) — strict invoice lookup;\n  - `SERVICE<module><id>` (e.g. `SERVICEvps12345`) — picks the most recent unpaid invoice for that service;\n  - `RINV<module><rid>` (e.g. `RINVvps78901`) — picks the most recent unpaid invoice for that repeat-invoice row;\n  - `PREPAYID<pid>INV<iid>` — explicit prepay-funding invoice.\n\n**Query params:**\n- `redirectUrl` (string, optional) — override the gateway return-URL. Defaults to `https://my.interserver.net/pay/`.\n\n**Returns** (one of three shapes — branch on `type`):\n- **type=`redirect`:** `{type: \"redirect\", redirect: \"<gateway-url>\", text: \"...\"}` — send the user to `redirect`.\n- **type=`submit`:** `{type: \"submit\", action: \"<url>\", method: \"POST\", items: {field: value, ...}}` — render a form with those fields, POST to `action`.\n- **type=`single`:** `{type: \"single\", text: \"...\"}` — payment already processed; surface `text` to the customer.\n\n**Side effects:**\n- Creates a `payment_requests` row tracking the attempt (via `addPaymentRequest`).\n- On `single`-mode success (`cc`, `prepay`): marks the underlying `invoices.invoices_paid=1`, triggers `queue_process_payment($iid)` → service activation.\n- On `redirect`/`submit`-mode: nothing is paid yet; the gateway IPN/callback handler in `confirm()` (in each `Pay/*.php` subclass) runs `queue_process_payment` after the gateway notifies us of success.\n\n**Auth:** Session/API key. Ownership of every referenced invoice is enforced through the `setInvoices()` lookup (filters by session `account_id`).\n\n**Errors:**\n- `400 Invalid payment method` — unrecognized `method`.\n- `402` / gateway-specific text — card declined, balance insufficient, etc. Returned as `{error: \"<text>\"}`.\n- `422 Invalid Invoice Tag` — identifier format not recognized.\n- `401` — unauthenticated.\n- Method-specific:\n  - `cc`: card not verified (use `addBillingCreditCard` → `patchBillingCreditCardVerify` → `postBillingCreditCardVerify` first; verify via `updateBillingPaymentMethod`).\n  - `prepay`: insufficient prepay balance (use `addBillingPrepay` to top up first).\n\n**Related calls:**\n- **Get an invoice id to pass:** `addVps` / `addQs` / `addBackup` / `addMail` / `addBillingPrepay` / `getBillingInvoices`.\n- **Confirm invoice detail first:** `getBillingInvoice`.\n- **Set up payment methods:** `addBillingCreditCard`, `patchBillingCreditCardVerify`, `postBillingCreditCardVerify`, `updateBillingPaymentMethod`.\n- **After payment:** poll the originating service endpoint (e.g. `getVpsInfo` for VPS) until status flips to `active`.\n\n**Example happy-path (VPS):**\n```text\n# 1) Order created — POST /vps/order returned {serviceid: 12345, real_iids: [\"25296600\"]}\n# 2) Pay with stored credit card:\nGET /apiv2/billing/pay/cc/25296600\n# 3) Response:\n{\"type\": \"single\", \"text\": \"Payment processed.\"}\n# 4) Poll service:\nGET /apiv2/vps/12345  -> {\"vps_status\": \"active\", ...}\n```\n**Example PayPal flow:**\n```text\nGET /apiv2/billing/pay/paypal/25296600\n{\"type\": \"redirect\", \"redirect\": \"https://www.paypal.com/...\", \"text\": \"...\"}\n# Client redirects user; PayPal IPN later marks invoice paid and activates service.\n```\n"
      },
      "parameters": [
        {
          "name": "method",
          "description": "The payment method to use. Valid values: `cc` (credit card), `paypal`, `prepay`, `payssion`, `payu`, `ccavenue`, `cashfree`, `coinbase`, `btcpay`.",
          "schema": {
            "enum": [
              "cc",
              "paypal",
              "prepay",
              "payssion",
              "payu",
              "ccavenue",
              "cashfree",
              "coinbase",
              "btcpay"
            ],
            "type": "string"
          },
          "in": "path",
          "required": true
        },
        {
          "name": "invoices",
          "description": "A comma-separated list of invoice IDs or invoice Tags to pay. These IDs are returned by order endpoints (e.g. `/backups/order`, `/vps/order`) and by `/billing/invoices`. Invoice tags accepted are SERVICE<module><id>, RINV<module><repeat invoice id>, INV<module><invoice id>, PREPAY<prepay id><invoice id>, and <invoice id>.",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/ping": {
      "get": {
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                },
                "examples": {
                  "pingResponse": {
                    "value": "\"pong\""
                  }
                }
              }
            },
            "description": "A simple response of \"pong\" for use with simple tests to see if a service is up. "
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Something is wrong"
          }
        },
        "security": [
          {}
        ],
        "operationId": "pingServer",
        "summary": "Liveness check — returns the JSON string \"pong\" to confirm API is up",
        "description": "Trivial GET that returns the JSON string `\"pong\"` so AI agents and monitors can verify the API endpoint is reachable. Public — no auth required, no params, no body. Does not exercise the database, queue, or any plugin modules. Sibling ops: `getInfo` (richer probe that touches MySQL).\n\n**Path/Query/Body:** None.\n\n**Returns:** JSON-encoded string `\"pong\"` with HTTP 200.\n\n**Auth:** None (public endpoint).\n\n**Errors:** No documented error path under normal operation — a non-200 or absent response indicates the API is down, the host is unreachable, or upstream routing is broken.\n\n**Use when:**\n- Bootstrapping a new client and want to confirm the API is reachable.\n- Smoke-testing in CI/health-check pipelines.\n- Diagnosing connectivity issues before higher-cost calls.\n\n**Related calls:**\n- **Deeper health probe:** `getInfo` (exercises the DB layer).\n"
      }
    },
    "/qs": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/QuickserverRow"
                  }
                }
              }
            },
            "links": {
              "GetQuickServerDetails": {
                "operationId": "getQsInfo",
                "parameters": {
                  "id": "$response.body#/0/qs_id"
                },
                "description": "Use the `qs_id` from any item in the response array to fetch full QuickServer details."
              }
            },
            "description": "The listing of `Quickservers` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsList",
        "summary": "List QuickServer rapid-deploy dedicated servers on the account",
        "description": "Use to enumerate the caller's QuickServers (quick-provision physical dedicated boxes that share the VPS billing model). No params, no body. Each row has `qs_id`, `qs_name`, `qs_hostname`, `qs_status`, `qs_comment`, and `cost`. Feed `qs_id` into `getQsInfo` for full details, or any per-server action (`doQsStart`, `doQsStop`, `doQsRestart`, `getQsBackups`, etc.).\nReturns: array of QuickServer rows. Errors: 401 if unauthenticated.\nSiblings: `getVpsList` (virtual VPS surface), `getQsInfo`, `getNewQs` for ordering metadata."
      }
    },
    "/qs/order": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuickserverOrder"
                }
              }
            },
            "description": "Quickserver Ordering information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewQs",
        "summary": "Get QuickServer order form metadata and available plans/templates",
        "description": "Use before placing or validating a QuickServer order to retrieve pricing, available servers, OS templates, and form fields. Read-only — no params, no body, no charge.\nReturns: `QuickserverOrder` schema with plan/template/server options used to build the order payload for `putQs` (validate) or `addQs` (place). Errors: 401 if unauthenticated.\nSiblings: `putQs` (dry-run validation), `addQs` (commits and invoices), `getNewVps` (virtual VPS ordering surface)."
      },
      "put": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "Validate QuickServer Order response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putQs",
        "summary": "Validate a QuickServer order without charging or provisioning",
        "description": "Dry-run the order payload before calling `addQs`. No invoice is created and no service is provisioned. Use to surface form errors, compute the price, and resolve the chosen `server`/`os`/`distro` against the master pool.\nBody (form): `server` (master ID), `password`, `os` (template), `comment`, `tos`. Returns the `validate_buy_qs` result with `continue` flag, normalized fields, `service_cost`, and `errors` array. Errors: 401 if unauthenticated; validation errors are returned in the body, not as 4xx.\nSiblings: `addQs` (commits the order), `getNewQs` (form metadata), `putVps` (VPS equivalent)."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a QuickServer instance. Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addQs",
        "summary": "Place a QuickServer order, generating a real invoice and queuing provisioning",
        "description": "Commits the validated order: creates the service row, generates a real invoice, and queues provisioning. Body fields match `putQs` (`server`, `password`, `os`, `comment`, `tos`) — call `putQs` first to catch errors. On `validation.continue=false`, returns the joined error string with no charge.\nReturns: `ServiceOrderPostResponse` with the new service ID and invoice info. Pay via `getBillingInvoice`/`initiatePayment`. Errors: 401 if unauthenticated, 4xx with message on validation failure.\nSiblings: `putQs` (validate first), `getNewQs`, `addVps` (VPS equivalent)."
      }
    },
    "/qs/{id}": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quickserver"
                }
              }
            },
            "links": {
              "GetQuickServerInvoices": {
                "operationId": "getQsInvoices",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Retrieve billing invoices for this QuickServer."
              },
              "RestartQuickServer": {
                "operationId": "doQsRestart",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Restart this QuickServer."
              },
              "StartQuickServer": {
                "operationId": "doQsStart",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Power on this QuickServer."
              },
              "StopQuickServer": {
                "operationId": "doQsStop",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Power off this QuickServer."
              },
              "BackupQuickServer": {
                "operationId": "postQsBackup",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Create a backup of this QuickServer."
              },
              "ListQuickServerBackups": {
                "operationId": "getQsBackups",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "List available backups for this QuickServer."
              },
              "ReinstallQuickServerOS": {
                "operationId": "getQsReinstallOs",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "View available OS templates for reinstalling this QuickServer."
              },
              "GetReverseDns": {
                "operationId": "getQsReverseDns",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "View reverse DNS entries for this QuickServer."
              }
            },
            "description": "Quickserver details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsInfo",
        "summary": "Get full details for one QuickServer including credentials and links",
        "description": "Returns the QuickServer dashboard payload — service info, IPs, hostname, OS, status, billing, and the list of available `client_links` (action endpoints the caller is allowed to invoke). Path param: `id` (integer QuickServer ID).\nReturns: `Quickserver` schema. Use response links to drive `doQsStart`, `doQsStop`, `doQsRestart`, `getQsBackups`, `getQsReinstallOs`, `getQsReverseDns`, `getQsInvoices`. Errors: 401 if unauthenticated, 404 if `id` is not owned by caller.\nSiblings: `updateQsInfo` (mutate), `quickserversCancel` (delete), `getVpsInfo` (VPS equivalent)."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateQsInfo",
        "summary": "Update QuickServer order metadata or stored settings without OS impact",
        "description": "Mutates QuickServer-level settings (comment, stored notes) without affecting the running OS. Path param: `id`. Body fields are module-specific and processed by the shared `View::go` handler.\nReturns: `SuccessTextResponse`. Errors: 401 if unauthenticated, 404 if not owned by caller.\nFor server-side actions use the dedicated endpoints — hostname via `postQsChangeHostname`, password via `postQsChangeRootPassword`, OS via `postQsReinstallOs`. Siblings: `getQsInfo` (read), `quickserversCancel` (delete)."
      },
      "delete": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QuickserversCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "quickserversCancel",
        "summary": "Cancel a QuickServer service at the end of the current billing cycle",
        "description": "Schedules deprovisioning. The server keeps running until the current billing period ends, then is canceled and the recurring invoice stops. Path param: `id` (integer).\nReturns: `{ success: bool, text: string }`. Errors: 401 if unauthenticated, 404 if not owned by caller. Reversible only by support before the cycle closes — use `getQsInvoices` to check the next invoice date first.\nSiblings: `getQsInfo`, `VPSCancel` (VPS equivalent), `serversCancel` (dedicated equivalent)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/backup": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsBackup",
        "summary": "Queue creation of a new QuickServer backup snapshot (note: GET triggers job)",
        "description": "Note: GET on `/qs/{id}/backup` triggers a backup job — despite the verb, this is a state-changing action. Queues a `backup` operation; backup name is auto-generated. Path param: `id` (integer).\nReturns: `{ text, queueId }`. Async — backup completes in minutes to hours depending on disk size. Poll `getQsBackups` to see when it appears. Errors: 401 if unauthenticated, 404 if not owned by caller, 409 if status != `active`.\nSiblings: `getQsBackups` (list), `postQuickServerRestore`, `downloadQsBackup`, `deleteQsBackup`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/backups": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpsBackupRows"
                }
              }
            },
            "description": "The listing of available backups for the QuickServer."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsBackups",
        "summary": "List available QuickServer backups across Swift, MinIO, and ZFS storage",
        "description": "Returns all backups visible to the caller for this QuickServer across the three backup backends. Path param: `id` (integer). Optional query `all=1` lists every backup the customer owns, not just this server's.\nReturns: `VpsBackupRows` array — each row has `name`, `type` (swift/minio/zfs), `size`, `service`, `path`. Use `name` (not a numeric ID) with `downloadQsBackup` (PATCH), `deleteQsBackup` (DELETE), or `postQuickServerRestore`. Errors: 401, 404 if not owned by caller.\nSiblings: `getQsBackup` (create), `postQuickServerRestore`."
      },
      "delete": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "file",
            "description": "The backup filename to delete.",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteQsBackup",
        "summary": "Permanently delete a QuickServer backup file from object storage",
        "description": "Removes the backup from its storage backend. Irreversible — the backup cannot be recovered. Path param: `id`. Required: `file` (the backup `name` from `getQsBackups`, in query or form body).\nWorks for `swift` and `minio` backups; `zfs` snapshots cannot be deleted via this endpoint (returns an error pointing to support). Returns: `SuccessTextResponse` with the removed name. Errors: 401, 404 if not owned, error message if backup type is unsupported or the storage operation fails.\nSiblings: `getQsBackups` (list), `downloadQsBackup` (PATCH), `postQuickServerRestore`."
      },
      "patch": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "description": "The backup filename to download.",
                    "type": "string"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "description": "The backup filename to download.",
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "text": {
                      "type": "string"
                    },
                    "url": {
                      "description": "A pre-signed download URL valid for 24 hours.",
                      "type": "string"
                    }
                  }
                }
              }
            },
            "description": "Download URL for the backup file."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "downloadQsBackup",
        "summary": "Generate a 24-hour pre-signed download URL for a QuickServer backup",
        "description": "Returns a temporary signed URL to fetch the backup directly from object storage. Path param: `id`. Body (JSON or form): `file` (the backup `name` from `getQsBackups`).\nOnly available for `minio`-type backups; `swift` and `zfs` backups return an error directing the caller to contact support. URL expires in 24 hours. Returns: `{ text, url }`. Errors: 401, 404 if not owned, error message for unsupported backup type or sharing failure.\nSiblings: `getQsBackups` (list, get `name`), `deleteQsBackup`, `postQuickServerRestore`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        },
        {
          "name": "all",
          "description": "Set to `1` to list all backups across all services, not just the ones for the given QuickServer.",
          "schema": {
            "enum": [
              "0",
              "1"
            ],
            "type": "string"
          },
          "in": "query"
        }
      ]
    },
    "/qs/{id}/block_smtp": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsBlockSmtp",
        "summary": "Block outbound SMTP traffic on a QuickServer to halt mail abuse",
        "description": "Queues a firewall rule that drops outbound port 25 traffic, used to halt spam/abuse without taking the server offline. Path param: `id` (integer). No body.\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes via the queue worker, which also re-runs VNC setup. Errors: 401, 404 if not owned by caller, 409 if status != `active`. Reversible only by support — there is no `unblock_smtp` endpoint.\nSiblings: `doVpsBlockSmtp`, `getQsInfo`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/change_hostname": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "QuickServer Change Hostname info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsChangeHostname",
        "summary": "Get current QuickServer hostname plus change rules and platform support",
        "description": "Read-only probe before calling `postQsChangeHostname`. Path param: `id` (integer). Returns the current hostname and the validation rules the new hostname must satisfy.\nReturns: object with hostname metadata. Errors: 401, 404 if not owned by caller, 409 if status != `active`. Note: hostname changes are only supported on OpenVZ/Virtuozzo platforms — `postQsChangeHostname` rejects KVM/dedicated types with an explanatory error.\nSiblings: `postQsChangeHostname`, `getVpsChangeHostname`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsChangeHostname",
        "summary": "Change a QuickServer's system hostname (OpenVZ/Virtuozzo only)",
        "description": "Updates the hostname and the matching reverse DNS entry. Path param: `id`. Body (JSON or form): `hostname` (must pass `valid_hostname`, must differ from current).\nOnly supported on OpenVZ/Virtuozzo platforms — KVM/dedicated returns a 4xx with a contact-support message. Pending services update the DB row directly (`{ text }`); active services queue the change (`{ text, queueId }`, ~2 min). Errors: 401, 404 if not owned, 409 if status != `active`, validation error for bad hostname or no change.\nSiblings: `getQsChangeHostname`, `postVpsChangeHostname`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/change_root_password": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "QuickServer Change Root Password response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsChangeRootPassword",
        "summary": "Get metadata for QuickServer root/OS password change requirements",
        "description": "Read-only probe before calling `postQsChangeRootPassword`. Path param: `id` (integer). Use to surface password complexity rules and confirm the QuickServer accepts root password changes.\nReturns: object with reset metadata. Errors: 401, 404 if not owned by caller, 409 if status != `active`.\nNote: this changes the OS root password (Linux) — for the Webuzo control panel password use `postQsChangeWebuzoPassword`. Siblings: `postQsChangeRootPassword`, `postQsResetPassword` (random password), `getVpsChangeRootPassword`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsChangeRootPassword",
        "summary": "Change QuickServer root/administrator password to a chosen value",
        "description": "Queues a root password change. Path param: `id`. Body (JSON or form): `password` (the new password — required, no server-side complexity validation here).\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes. Both queue and history entries are written. Errors: 401, 404 if not owned, 409 if status != `active`, 400 if `password` is missing.\nFor a randomly generated password use `postQsResetPassword` instead. For Webuzo panel password use `postQsChangeWebuzoPassword`. Siblings: `getQsChangeRootPassword`, `postVpsChangeRootPassword`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/change_timezone": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "TimeZones": {
                    "value": [
                      "America/New_York",
                      "America/Nome",
                      "America/Noronha",
                      "America/North_Dakota/Beulah",
                      "America/North_Dakota/Center"
                    ]
                  }
                }
              }
            },
            "description": "QuickServer Change Timezone info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsChangeTimezone",
        "summary": "List timezones the QuickServer can be set to via change_timezone",
        "description": "Returns the system timezone catalog (parsed from `/usr/share/zoneinfo/zone.tab`) for use with `postQsChangeTimezone`. Path param: `id` (integer). Read-only — no queue, no charge.\nReturns: array of timezone strings (e.g. `America/New_York`, `Europe/London`). Errors: 401, 404 if not owned by caller, 409 if status != `active` (handler labels these errors as `Invalid VPS Passed` / `VPS is not active` due to shared code).\nSiblings: `postQsChangeTimezone` (commit), `getVpsChangeTimezone`, `getQsChangeHostname` (also informational)."
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimezoneUpdate"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimezoneUpdate"
              }
            }
          },
          "required": true
        },
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsChangeTimezone",
        "summary": "Change the system timezone on a QuickServer to a catalog entry",
        "description": "Queues a timezone change. Path param: `id`. Body (JSON or form): `timezone` (must be one of the strings returned by `getQsChangeTimezone`).\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes by the queue worker. Errors: 401, 404 if not owned, 409 if status != `active`, 422 if `timezone` is not in the catalog.\nSiblings: `getQsChangeTimezone` (call first to get valid options), `postVpsChangeTimezone`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/change_webuzo_password": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "QuickServer Change Webuzo Password info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsChangeWebuzoPassword",
        "summary": "Get metadata for changing the Webuzo control panel admin password",
        "description": "Read-only probe before `postQsChangeWebuzoPassword`. Path param: `id` (integer). Webuzo is a control panel optionally installed on QuickServers — its admin password is separate from the OS root password.\nReturns: object with change instructions. Errors: 401, 404 if not owned by caller, 409 if status != `active`.\nSiblings: `postQsChangeWebuzoPassword`, `postQsChangeRootPassword` (OS root password), `postQsResetPassword`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsChangeWebuzoPassword",
        "summary": "Change Webuzo control panel admin password live (synchronous, not queued)",
        "description": "Calls the Webuzo SDK directly on the server to change the panel `admin` password, then emails the new credentials. Path param: `id`. Body: `password` (new Webuzo password, must pass `valid_password`), `login_password` (caller's account login password — verified via md5 hash).\nSynchronous — no queue ID. Requires a prior Webuzo-Details history entry. Returns: success message string. Errors: 401, 404 if not owned, 409 if status != `active`, validation errors for missing fields, wrong login password, weak new password, or SDK failure.\nSiblings: `getQsChangeWebuzoPassword`, `postQsChangeRootPassword` (OS root)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/disable_cd": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsDisableCd",
        "summary": "Disable the virtual CD/DVD drive device on a QuickServer",
        "description": "Queues removal of the virtual CD/DVD device from the QuickServer (full disable, not just eject). Path param: `id` (integer). No body.\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes; queue worker also re-runs VNC setup. Errors: 401, 404 if not owned by caller, 409 if status != `active`.\nSiblings: `doQsEjectCd` (eject the ISO but keep drive), `postQsInsertCd` (mount an ISO), `getQsInsertCd` (list available ISOs)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/disable_quota": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsDisableQuota",
        "summary": "Disable disk-quota enforcement at OS level on a QuickServer",
        "description": "Queues a job to turn off disk-quota enforcement at the OS level. Use when quota errors block legitimate writes or before resizing disk space. Path param: `id` (integer). No body.\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes; queue worker also re-runs VNC setup. Errors: 401, 404 if not owned by caller, 409 if status != `active`. Re-enable later with `doQsEnableQuota`.\nSiblings: `doQsEnableQuota` (re-enable), `doVpsDisableQuota` (VPS equivalent)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/eject_cd": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsEjectCd",
        "summary": "Eject the currently mounted ISO from a QuickServer's virtual CD drive",
        "description": "Queues an eject — drive remains attached but no media. Path param: `id` (integer). No body.\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes. The queue worker also re-runs VNC setup so the console reflects the change. Errors: 401, 404 if `id` is not owned by caller. Note: this handler does not validate `active` status.\nSiblings: `postQsInsertCd` (mount an ISO), `getQsInsertCd` (list ISOs), `doQsDisableCd` (remove the drive itself)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/enable_quota": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsEnableQuota",
        "summary": "Enable disk-quota enforcement at OS level on a QuickServer",
        "description": "Queues a job to turn on disk-quota enforcement at the OS level. Pair with `doQsDisableQuota` when re-enabling after maintenance, disk resizing, or restoring a backup. Path param: `id` (integer). No body.\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes; queue worker also re-runs VNC setup. Errors: 401, 404 if not owned by caller, 409 if status != `active`.\nSiblings: `doQsDisableQuota` (turn off), `doVpsEnableQuota` (VPS equivalent)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/insert_cd": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "QuickServer Insert CD info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsInsertCd",
        "summary": "List ISO images available to mount on a QuickServer's virtual CD",
        "description": "Returns the catalog of bootable ISOs the caller can mount via `postQsInsertCd`. Path param: `id` (integer). Read-only — no queue, no charge.\nReturns: object with available ISO entries (URLs/labels) keyed for the QuickServer's hardware type. Errors: 401 if unauthenticated. Note: this handler does not validate ownership or active status — pair with `getQsInfo` first if you need those checks before presenting options to a user.\nSiblings: `postQsInsertCd` (mount the chosen URL), `doQsEjectCd`, `doQsDisableCd`, `getVpsInsertCd`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsInsertCd",
        "summary": "Mount an ISO image as the QuickServer's virtual CD via URL",
        "description": "Queues an `insert_cd` job that attaches the given ISO URL to the QuickServer's virtual CD drive (typically for OS reinstalls or rescue boots). Path param: `id`. Body (JSON or form): `url` (the ISO URL — pick one from `getQsInsertCd`).\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes. Errors: 401, 404 if not owned by caller. The action is idempotent in effect (latest mount wins).\nSiblings: `getQsInsertCd` (list options), `doQsEjectCd` (unmount), `doQsDisableCd`, `postQsReinstallOs` (template-based)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/invoices": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsInvoices",
        "summary": "List billing invoices charged for one QuickServer service",
        "description": "Returns invoices charged for this QuickServer (initial setup + recurring). Path param: `id` (integer).\nReturns: `ChargeInvoiceRows` — each row has invoice ID, amount, status (paid/unpaid), date. Use the invoice ID with `getBillingInvoice` for full detail or `initiatePayment` to settle. Errors: 401 if unauthenticated, 404 if not owned by caller.\nSiblings: `getQsInfo`, `getVpsInvoices`, `getBillingInvoice`, `quickserversCancel` (check next-invoice date before canceling)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/reinstall_os": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpsTemplatesList"
                }
              }
            },
            "description": "QuickServer Reinstall OS info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsReinstallOs",
        "summary": "List OS templates available for a QuickServer reinstall",
        "description": "Returns the OS template catalog filtered to the QuickServer's hardware/template type. Path param: `id` (integer). Read-only — no provisioning happens.\nReturns: `{ templates: [...] }` — each template has `template_file`, `template_name`, `template_version`. Use `template_file` with `postQsReinstallOs`. Non-admin callers only see templates with `template_available=1`. Errors: 401 if unauthenticated.\nSiblings: `postQsReinstallOs` (commit, destructive), `getVpsReinstallOs`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsReinstallOs",
        "summary": "Reinstall the operating system on a QuickServer (DESTRUCTIVE — wipes disk)",
        "description": "Wipes the disk and reinstalls the chosen OS template. All data, configs, and snapshots are erased. Path param: `id`. Body: `template` (a `template_file` from `getQsReinstallOs`), `password` (new root password — required for non-Windows templates).\nFor active services, queues `reinstall_os` (~2 min). For inactive services, just stores the OS preference for next activation. Updates `qs_status` to `Reinstalling` and clears screenshots. Returns flash messages — typical envelope. Errors: 401, invalid template name returns error flash.\nSiblings: `getQsReinstallOs` (list options), `postVpsReinstallOs`, `postQuickServerRestore` (recover from backup instead)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/reset_password": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "QuickServer Reset password info"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsResetPassword",
        "summary": "Get options for QuickServer randomized root password reset",
        "description": "Read-only probe before `postQsResetPassword`. Path param: `id` (integer). Use to confirm the QuickServer is in a state that allows password resets.\nReturns: object with reset configuration. Errors: 401, 404 if not owned by caller, 409 if status != `active`.\nNote: `postQsResetPassword` generates a random password — for a chosen value use `postQsChangeRootPassword`. Siblings: `postQsResetPassword`, `postQsChangeRootPassword`, `getVpsResetPassword`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsResetPassword",
        "summary": "Reset QuickServer root password to a server-generated random value",
        "description": "Queues a `reset_password` job that generates a new root password and emails it to the account owner. Path param: `id` (integer). No body — password is generated server-side.\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes. Errors: 401, 404 if not owned by caller, 409 if status != `active`.\nFor a chosen password use `postQsChangeRootPassword` instead; for the Webuzo panel password use `postQsChangeWebuzoPassword`. Siblings: `getQsResetPassword`, `postVpsResetPassword`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/restart": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsRestart",
        "summary": "Reboot a QuickServer with a graceful OS-level restart",
        "description": "Queues a graceful restart — equivalent to `reboot` inside the OS. Path param: `id` (integer). No body. Use to recover from a hung service or apply pending kernel/config changes.\nReturns: `{ text, queueId }`. Async — server is back online within ~2 minutes; queue worker also re-runs VNC setup. Errors: 401, 404 if not owned by caller. Note: handler does not gate on `active` status — restarts work even on suspended services.\nSiblings: `doQsStart`, `doQsStop`, `doVpsRestart`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/reverse_dns": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReverseDnsEntries"
                }
              }
            },
            "description": "QuickServer Reverse DNS info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsReverseDns",
        "summary": "Get reverse DNS (PTR) records for all of a QuickServer's IPs",
        "description": "Returns the current PTR record for the primary IP and any additional IPs assigned to the QuickServer. Path param: `id` (integer). Read-only — looks up live DNS, no queue.\nReturns: `{ ips: { \"<ip>\": \"<hostname>\", ... } }`. Use the keys with `postQsReverseDns` to update entries. Errors: 401 if unauthenticated. Note: handler does not gate on ownership/active status.\nSiblings: `postQsReverseDns`, `getVpsReverseDns`."
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            }
          },
          "required": true
        },
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextResponse"
                }
              }
            },
            "description": "Update QuickServer Reverse DNS response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsReverseDns",
        "summary": "Update reverse DNS (PTR) records for a QuickServer's IPs",
        "description": "Sets PTR records for one or more of the QuickServer's IPs. Path param: `id`. Body (form): `ips` — keyed by IP, value is the desired hostname (must be valid).\nReturns: `{ message: \"DNS Updated\", success: true }`. Caveat: in the current implementation the body is parsed but the per-IP update loop is a no-op shell — verify with `getQsReverseDns` after calling, and use the support channel if changes don't propagate. Errors: 401 if unauthenticated.\nSiblings: `getQsReverseDns`, `postVpsReverseDns`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/setup_vnc": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "Get QuickServer Setup VNC Information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsSetupVnc",
        "summary": "Get current VNC console connection details for a QuickServer",
        "description": "Read-only probe for the VNC tunnel that exposes the server's console (host, port, credentials). Path param: `id` (integer).\nReturns: object with VNC connection info. Errors: 401 if unauthenticated, 404 if `id` is not owned by caller, 409 if service is not `active`. Note: this endpoint is currently a stub — the `// todo: return vnc info` line indicates the response body may be empty until completed.\nSiblings: `postQsSetupVnc` (configure access IP), `getVpsSetupVnc`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsSetupVnc",
        "summary": "Configure the source IP allowed to reach a QuickServer's VNC console",
        "description": "Sets the IP allowed to reach the VNC tunnel and queues a `setup_vnc` to apply it. Path param: `id`. Body (JSON or form): `vnc` (a valid IPv4 address — only this address can reach the console).\nReturns: `{ text, queueId }`. Async — applied within ~2 minutes. Errors: 401, 404 if not owned, 409 if status != `active`. Returns an inline `Invalid IP` message when `vnc` fails `validIp`. The VPS-style helper also runs after the DB update.\nSiblings: `getQsSetupVnc` (read), `postVpsSetupVnc`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/start": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsStart",
        "summary": "Power on a QuickServer that is currently stopped or pending boot",
        "description": "Queues a `start` command to bring the QuickServer online. Path param: `id` (integer). No body. Idempotent in practice — re-running on an already-on server is a no-op at the worker.\nReturns: `{ text, queueId }`. Async — typically online within ~2 minutes; queue worker re-runs VNC setup. Errors: 401, 404 if not owned by caller. Note: handler does not gate on status, so it can be issued even for non-active services.\nSiblings: `doQsStop`, `doQsRestart`, `getQsInfo`, `doVpsStart`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/stop": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "QuickServer ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doQsStop",
        "summary": "Power off a QuickServer with a graceful shutdown command",
        "description": "Queues a `stop` command. Path param: `id` (integer). No body. Use before maintenance, snapshot, or to halt traffic — billing continues regardless of power state, so use `quickserversCancel` to also stop charges.\nReturns: `{ text, queueId }`. Async — typically off within ~2 minutes; queue worker re-runs VNC setup. Errors: 401, 404 if not owned by caller. Note: handler does not gate on status.\nSiblings: `doQsStart`, `doQsRestart`, `doVpsStop`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/traffic_usage": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "Get QuickServer Traffic usage"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsTrafficUsage",
        "summary": "Get bandwidth usage for the QuickServer's current billing period",
        "description": "Returns the inbound/outbound bandwidth totals and time-series points for the QuickServer's current cycle. Path param: `id` (integer). Read-only.\nReturns: bandwidth-data object from `qs_bandwidth_data` (totals, daily/hourly points, overage flag). Errors: 401 if unauthenticated. Note: handler does not gate on ownership or active status.\nSiblings: `postQsTrafficUsage` (same data, accessible via POST for filtered queries), `getVpsTrafficUsage`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "Submit QuickServer Traffic Usage"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsTrafficUsage",
        "summary": "Query QuickServer bandwidth usage via POST (filtered variant)",
        "description": "Functional duplicate of `getQsTrafficUsage` exposed under POST so clients can pass a filter body. Path param: `id` (integer). Body fields are accepted but the current handler ignores them and returns the full current-cycle dataset.\nReturns: same bandwidth-data object as `getQsTrafficUsage`. Errors: 401 if unauthenticated. No active-status or ownership gate.\nSiblings: `getQsTrafficUsage`, `postVpsTrafficUsage`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/view_desktop": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "Get QuickServer View Desktop Information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsViewDesktop",
        "summary": "Get the full QuickServer dashboard view payload (rich format)",
        "description": "Returns the same rich payload the AdminLTE UI uses — service info, billing, available client_links, resource graphs. Heavier than `getQsInfo` and intended for desktop dashboards. Path param: `id` (integer).\nReturns: object with `serviceInfo`, `client_links`, etc. (admin-only fields stripped). Errors: 401 if unauthenticated. Note: handler does not gate on ownership/active status.\nSiblings: `getQsInfo` (lighter), `postQsViewDesktop` (mutate variant), `getVpsViewDesktop`."
      },
      "post": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "description": "Submit QuickServer View Desktop Information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQsViewDesktop",
        "summary": "Submit changes and re-fetch the QuickServer dashboard view payload",
        "description": "Same handler as `getQsViewDesktop` but accessible via POST so callers can pass body fields alongside re-fetching the view. Path param: `id`. Body fields are accepted by the underlying View handler.\nReturns: refreshed dashboard object — `serviceInfo`, `client_links`, etc. Errors: 401 if unauthenticated.\nFor structured updates prefer the dedicated endpoints (`postQsChangeHostname`, `postQsReverseDns`, `postQsSetupVnc`, etc.) which return queue IDs. Siblings: `getQsViewDesktop`, `postVpsViewDesktop`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/scrub_ips": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/scrubIpsLists"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "getScrubIpsList",
        "summary": "List all Scrub IP DDoS protection services on the authenticated account",
        "description": "Returns every Scrub IP service belonging to the authenticated customer with status, protected IP, plan name, and recurring cost. Use this for dashboards, picking a service ID for downstream calls (getScrubIpDetails, enableScrub, createRule, getScrubIpLogs), or auditing which IPs are routed through DDoS scrubbing. No path/query/body parameters; service ownership is enforced via session account_id. Returns an array of {id, repeat_invoices_cost, ip, status, services_name}; empty array if no scrub services. Errors: 401 unauthenticated. Caveat: only customer-owned services are visible. Siblings: getScrubIpDetails, getOrderDetail, placeScrubOrder, cancelScrubIp."
      }
    },
    "/scrub_ips/{id}": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "serviceInfo": {
                      "type": "object",
                      "properties": {
                        "scrub_ip_id": {
                          "type": "string"
                        },
                        "scrub_ip_type": {
                          "type": "string"
                        },
                        "scrub_ip_custid": {
                          "type": "string"
                        },
                        "scrub_ip_order_date": {
                          "type": "string"
                        },
                        "scrub_ip_ip": {
                          "type": "string"
                        },
                        "scrub_ip_service_id": {
                          "type": "string"
                        },
                        "scrub_ip_service_module": {
                          "type": "string"
                        },
                        "scrub_ip_status": {
                          "enum": [
                            "active",
                            "pending",
                            "canceled",
                            "expired"
                          ],
                          "type": "string"
                        },
                        "scrub_ip_invoice": {
                          "type": "string"
                        },
                        "scrub_ip_currency": {
                          "type": "string"
                        },
                        "scrub_ip_coupon": {
                          "type": "string"
                        },
                        "scrub_ip_comment": {
                          "type": "string"
                        }
                      }
                    },
                    "client_links": {
                      "type": "array",
                      "items": {
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "link": {
                            "type": "string"
                          },
                          "icon": {
                            "type": "string"
                          },
                          "icon_text": {
                            "type": "string"
                          },
                          "help_text": {
                            "type": "string"
                          },
                          "other_attr": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "billingDetails": {
                      "type": "object",
                      "properties": {
                        "service_last_invoice_date": {
                          "type": "string"
                        },
                        "service_payment_status": {
                          "type": "string"
                        },
                        "service_frequency": {
                          "type": "string"
                        },
                        "next_date": {
                          "type": "string"
                        },
                        "service_next_invoice_date": {
                          "type": "string"
                        },
                        "service_currency": {
                          "type": "string"
                        },
                        "service_currency_symbol": {
                          "type": "string"
                        },
                        "service_cost_info": {
                          "type": "string"
                        }
                      }
                    },
                    "custCurrency": {
                      "type": "string"
                    },
                    "custCurrencySymbol": {
                      "type": "string"
                    },
                    "package": {
                      "type": "string"
                    },
                    "extraInfoTables": {
                      "type": "object",
                      "properties": {
                        "scrub_ips": {
                          "type": "object",
                          "properties": {
                            "title": {
                              "type": "string"
                            },
                            "rows": {
                              "type": "array",
                              "items": {
                                "properties": {
                                  "desc": {
                                    "type": "string"
                                  },
                                  "value": {
                                    "type": "string"
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "filter_firewall": {
                      "type": "object",
                      "properties": {
                        "rules": {
                          "type": "array",
                          "items": {
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "source_ip": {
                                "type": "string"
                              },
                              "destination_ip": {
                                "type": "string"
                              },
                              "protocol_id": {
                                "type": "string"
                              },
                              "source_port": {
                                "type": "string"
                              },
                              "destination_port": {
                                "type": "string"
                              },
                              "xdp_action": {
                                "type": "string"
                              },
                              "global_drop": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "filters": {
                          "type": "array",
                          "items": {
                            "properties": {
                              "daddr": {
                                "type": "string"
                              },
                              "dest": {
                                "type": "string"
                              },
                              "filter_name": {
                                "type": "string"
                              },
                              "destination_ip": {
                                "type": "string"
                              },
                              "filter": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "scrub_enabled": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "example": {
                    "serviceInfo": {
                      "scrub_ip_id": "123",
                      "scrub_ip_type": "11552",
                      "scrub_ip_custid": "456",
                      "scrub_ip_order_date": "2025-12-26 08:24:02",
                      "scrub_ip_ip": "11.24.11.23",
                      "scrub_ip_service_id": "11111",
                      "scrub_ip_service_module": "servers",
                      "scrub_ip_status": "active",
                      "scrub_ip_invoice": "654321",
                      "scrub_ip_currency": "USD",
                      "scrub_ip_coupon": "0",
                      "scrub_ip_comment": ""
                    },
                    "client_links": [
                      {
                        "label": "Invoices",
                        "link": "invoices",
                        "icon": "fas fa-file-invoice-dollar fa-w-12",
                        "icon_text": "",
                        "help_text": "Invoice History"
                      },
                      {
                        "label": "Cancel Scrub IPs",
                        "link": "cancel",
                        "icon": "fas fa-times",
                        "icon_text": "",
                        "help_text": "Cancel Scrub IPs"
                      },
                      {
                        "label": "Disable Scrub",
                        "link": "scrub_action",
                        "icon": "fa fa-shield text-lg",
                        "icon_text": "",
                        "help_text": "Enable/Disable Scrub",
                        "other_attr": ""
                      },
                      {
                        "label": "Scrub Documentation",
                        "link": "https://www.interserver.net/tips/kb/scrub/",
                        "icon": "fa fa-file text-lg",
                        "icon_text": "",
                        "help_text": "Scrub Documentation",
                        "other_attr": "target= \"_blank\""
                      }
                    ],
                    "billingDetails": {
                      "service_last_invoice_date": "December 26, 2025",
                      "service_payment_status": "Paid",
                      "service_frequency": "Monthly",
                      "next_date": "2026-01-26 08:24:02",
                      "service_next_invoice_date": "January 26, 2026",
                      "service_currency": "USD",
                      "service_currency_symbol": "$",
                      "service_cost_info": "5.00"
                    },
                    "custCurrency": "USD",
                    "custCurrencySymbol": "$",
                    "package": "Current IP + Scrub",
                    "extraInfoTables": {
                      "scrub_ips": {
                        "title": "Connection Information",
                        "rows": [
                          {
                            "desc": "IP",
                            "value": "11.12.12.12"
                          },
                          {
                            "desc": "Scrub",
                            "value": "Enabled"
                          }
                        ]
                      }
                    },
                    "filter_firewall": {
                      "rules": [],
                      "filters": [
                        {
                          "daddr": "2331742347",
                          "dest": "80",
                          "filter_name": "dns",
                          "destination_ip": "11.12.12.12",
                          "filter": "Dns"
                        },
                        {
                          "daddr": "2331742347",
                          "dest": "443",
                          "filter_name": "dns",
                          "destination_ip": "11.12.12.12",
                          "filter": "Dns"
                        }
                      ],
                      "scrub_enabled": 21104
                    }
                  }
                }
              }
            },
            "links": {
              "GetScrubIpInvoices": {
                "operationId": "getScrubIpInvoices",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Retrieve billing invoices for this Scrub IP service."
              },
              "EnableScrub": {
                "operationId": "enableScrub",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Enable DDoS scrubbing protection on this IP."
              },
              "DisableScrub": {
                "operationId": "disableScrub",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Disable DDoS scrubbing protection on this IP."
              },
              "GetScrubIpLogs": {
                "operationId": "getScrubIpLogs",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "View activity logs for this Scrub IP service."
              }
            },
            "description": "Scrub IP service details including firewall rules and filters."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getScrubIpDetails",
        "summary": "Get full Scrub IP service detail (rules + geo + filters)",
        "description": "Returns the full service-detail payload for one Scrub IP — used to render the dashboard or before mutating rules/filters. Includes `serviceInfo` (status, scrubbed IP, custid), `billingDetails` (cost, frequency), `client_links` (allowed self-service actions), and `filter_firewall` with the active firewall `rules`, geographic `geo_rules`, and traffic `filters`. Each rule/filter row carries its own `id` used by the delete endpoints. Sibling ops: `getScrubIpsList`, `enableScrub`, `disableScrub`, `createRule`, `scrubIpsDeleteRule`, `createGeoRule`, `scrubIpsDeleteGeoRule`, `createFilter`, `deleteFilter`, `getScrubIpInvoices`, `getScrubIpLogs`, `cancelScrubIp`.\n\n**Path:** `id` (integer, required) — service ID from `getScrubIpsList`.\n\n**Body / query:** None.\n\n**Returns:** object with `serviceInfo`, `billingDetails`, `client_links`, `filter_firewall` (`rules` / `geo_rules` / `filters`).\n\n**Auth:** Session/API key. Ownership enforced via `scrub_ips_custid`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `Invalid Service` — `id` is not owned by the session account.\n\n**Caveat:** rule/filter IDs are regenerated after recreate — re-fetch before calling a delete endpoint.\n\n**Related calls:**\n- **Mutations:** `enableScrub`, `disableScrub`, `createRule`, `createGeoRule`, `createFilter`.\n- **Deletes:** `scrubIpsDeleteRule`, `scrubIpsDeleteGeoRule`, `deleteFilter`.\n- **Billing / activity:** `getScrubIpInvoices`, `getScrubIpLogs`.\n- **Cancel:** `cancelScrubIp`.\n"
      },
      "delete": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "success",
                    "text"
                  ],
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "text": {
                      "type": "string",
                      "example": "Scrub Ips is canceled."
                    }
                  }
                }
              }
            },
            "description": "Request OK"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "cancelScrubIp",
        "summary": "Cancel a Scrub IP service and stop its recurring DDoS billing",
        "description": "Cancels the Scrub IP DDoS protection service. The protected IP is removed from the scrubbing infrastructure and the recurring invoice is closed; protection stops at end of the current billing cycle. Use only when the customer no longer needs DDoS scrubbing for the IP. Path param: `id` (integer, required) — service ID from getScrubIpsList. No request body. Returns {success: true, text: 'Scrub Ips is canceled.'}. Errors: 401 unauthenticated; 404/Invalid Service if id is not owned by the session account; 409 if the service is not in a cancellable state. Caveat: leaves the underlying VPS/server IP exposed to attacks once protection ends; contact billing for refund handling. Siblings: getScrubIpDetails, disableScrub, getScrubIpInvoices."
      },
      "parameters": [
        {
          "name": "id",
          "description": "ScrubIp ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/scrub_ips/{id}/invoices": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getScrubIpInvoices",
        "summary": "List recurring and one-time invoices billed for this Scrub IP service",
        "description": "Returns the recurring and one-time invoices generated for the Scrub IP service so the caller can verify billing status, present a payment history, or initiate payment on an unpaid invoice. Use after placeScrubOrder (to find the new invoice id) or before cancelScrubIp (to surface outstanding balance). Path param: `id` (integer, required) — service ID from getScrubIpsList. No body/query parameters. Returns ChargeInvoiceRows (array of invoice objects with id, amount, status, due dates). Errors: 401 unauthenticated; empty result if id is not owned by the session account. Caveat: paid invoices remain in history; filter on status client-side. Siblings: getScrubIpDetails, placeScrubOrder, cancelScrubIp."
      },
      "parameters": [
        {
          "name": "id",
          "description": "ScrubIp ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/scrub_ips/filter_types": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScrubIpFilterTypes"
                }
              }
            },
            "description": "Supported scrub filter types for building firewall rules."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getScrubIpFilterTypes",
        "summary": "List enabled traffic filter profiles available for createFilter",
        "description": "Returns the catalog of scrub filter profiles (e.g. dns, http, synproxy) currently enabled on the scrubbing platform, keyed by filter_name with a humanized display `name` and `desc`. Call this to populate a dropdown before invoking createFilter — the `filter_type` field on that endpoint must be one of the keys returned here. Not service-scoped: no path/query/body parameters and the same set applies to every Scrub IP. Returns {success: true, filters: {<filter_name>: {name, desc}, ...}}. Errors: 401 unauthenticated. Caveat: only filters with enabled=1 are returned; profile semantics are platform-defined (synproxy uses different request shape internally). Siblings: createFilter, deleteFilter, getScrubIpDetails."
      }
    },
    "/scrub_ips/{id}/enable": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "success",
                    "text"
                  ],
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "text": {
                      "type": "string",
                      "example": "Scrub is enabled on your IP."
                    }
                  }
                }
              }
            },
            "description": "Request OK"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "success",
                    "text"
                  ],
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "text": {
                      "type": "string",
                      "example": "Unable to enable Scrub on your IP."
                    }
                  }
                }
              }
            },
            "description": "Internal Server Error"
          }
        },
        "operationId": "enableScrub",
        "summary": "Enable DDoS scrubbing (BGP announcement) on the service's protected IP",
        "description": "Routes the service's protected IP through the Wanguard scrubbing infrastructure by creating a BGP announcement, so inbound traffic passes through filtering before reaching the backend. Call after placeScrubOrder activation, after disableScrub, or whenever the announcement was lost. Path param: `id` (integer, required) — service ID from getScrubIpsList. No request body (HTTP GET). Returns {success: true, text: 'Scrub is enabled on your IP.'} on 201 from Wanguard, persisted into the service's `extra` column. Errors: 400 Invalid Service if id is not owned by the session account; 401 unauthenticated; 500 if the upstream Wanguard call fails. Caveat: enabling re-routes live traffic and can briefly disrupt active sessions. Siblings: disableScrub, getScrubIpDetails, getScrubIpLogs."
      },
      "parameters": [
        {
          "name": "id",
          "description": "ScrubIp ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/scrub_ips/{id}/disable": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "success",
                    "text"
                  ],
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": true
                    },
                    "text": {
                      "type": "string",
                      "example": "Scrub is disabled on your IP."
                    }
                  }
                }
              }
            },
            "description": "Request OK"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "success",
                    "text"
                  ],
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "text": {
                      "type": "string",
                      "example": "Scrub is not enabled in this service."
                    }
                  }
                }
              }
            },
            "description": "Bad request"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "content": {
              "application/json": {
                "schema": {
                  "required": [
                    "success",
                    "text"
                  ],
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "text": {
                      "type": "string",
                      "example": "Unable to disable scrub on your IP."
                    }
                  }
                }
              }
            },
            "description": "Internal Server Error"
          }
        },
        "operationId": "disableScrub",
        "summary": "Disable DDoS scrubbing and remove the BGP announcement on the IP",
        "description": "Withdraws the BGP announcement from Wanguard so the IP stops being routed through scrubbing; traffic resumes flowing directly to the backend. Use for maintenance windows or migration off scrub. Path param: `id` (integer, required) — service ID from getScrubIpsList. No body (HTTP GET). The endpoint reads the stored Wanguard `href` from the service's `extra` JSON to know which announcement to delete; clears `extra` on success. Returns {success: true, text: 'Scrub is disabled on your IP.'}. Errors: 400 Invalid Service if id is not owned, or 'Scrub is not enabled in this service.' if there is no active announcement; 401 unauthenticated; 500 if upstream delete fails. Caveat: leaves the IP unprotected against DDoS until enableScrub is called. Siblings: enableScrub, cancelScrubIp, getScrubIpDetails."
      },
      "parameters": [
        {
          "name": "id",
          "description": "ScrubIp ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/scrub_ips/{id}/create_rule": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFirewallRule"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "ScrubIp ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "201": {
            "$ref": "#/components/responses/CreateFirewallResponse"
          },
          "400": {
            "$ref": "#/components/responses/CreateFirewall400Err"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/CreateFirewall500Err"
          }
        },
        "operationId": "createRule",
        "summary": "Add an L3/L4 firewall rule (allow/drop by IP, port, and protocol)",
        "description": "Creates an XDP firewall rule on the scrubber for the service's protected IP. Use to whitelist a known good source, block an abusive source, or restrict a destination port. Path param: `id` (integer, required) — service ID. Body (CreateFirewallRule): `source_ip` (IPv4, 0 = any), `source_port` (int, 0 = any), `destination_port` (int, 0 = any), `protocol_id` (1 ICMP or 2 TCP/UDP — must be 1 or 2), `xdp_action` (0 allow, 1 drop). Destination IP is locked to the service IP server-side. Returns 201 {success: true} when created. Errors: 400 with `errors[]` for invalid source_ip/protocol_id/xdp_action or Invalid Service; 401 unauthenticated; 500 if upstream Scrub::firewallCreate fails. Caveat: rules are stateless and may interact with active filters. Siblings: scrubIpsDeleteRule, createGeoRule, createFilter."
      }
    },
    "/scrub_ips/{id}/delete_rule": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "title": "Delete Firewall Rule",
                "description": "Delete firewall rule for your ip",
                "required": [
                  "rule_id"
                ],
                "type": "object",
                "properties": {
                  "rule_id": {
                    "type": "integer",
                    "example": 2045
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "ScrubIp ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/DeleteFirewallResponse"
          },
          "400": {
            "$ref": "#/components/responses/DeleteFirewall400Err"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/DeleteFirewall500Err"
          }
        },
        "operationId": "scrubIpsDeleteRule",
        "summary": "Delete an L3/L4 firewall rule by rule_id from getScrubIpDetails",
        "description": "Removes a previously created L3/L4 firewall rule from the Scrub IP service. The rule_id must come from the `filter_firewall.rules[].id` array returned by getScrubIpDetails — the endpoint validates the id belongs to this service before deleting. Path param: `id` (integer, required) — Scrub IP service ID. Body (JSON): {`rule_id`: integer, required}. Returns {success: true, text: 'Firewall Rule has been deleted.'}. Errors: 400 Invalid Service, 'rule_id is required.' or 'Invalid rule id' (rule does not belong to this service); 401 unauthenticated; 500 if upstream Scrub::firewallDelete fails. Caveat: if the rule was the only protection against a specific source, deleting it re-exposes the IP. Siblings: createRule, scrubIpsDeleteGeoRule, deleteFilter, getScrubIpDetails."
      }
    },
    "/scrub_ips/{id}/create_geo_rule": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateGeoFirewallRule"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "ScrubIp ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "201": {
            "$ref": "#/components/responses/CreateFirewallResponse"
          },
          "400": {
            "$ref": "#/components/responses/CreateGeoFirewall400Err"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/CreateFirewall500Err"
          }
        },
        "operationId": "createGeoRule",
        "summary": "Add a geographic firewall rule (block/allow by country code or ASN)",
        "description": "Creates a geo-based XDP rule on the scrubber for the service's protected IP. Use to block traffic from specific countries or ASNs (botnet source regions) or to allow only known regions. Path param: `id` (integer, required) — service ID. Body (CreateGeoFirewallRule): `country_code` (int, country numeric ID) OR `asn` (int) — at least one is required, `destination_port` (int, defaults 80), `xdp_action` (0 allow, 1 drop, defaults 1). Destination IP is locked to the service IP server-side. Returns 201 {success: true} when created. Errors: 400 errors[] 'Country or Asn is required.' or Invalid Service; 401 unauthenticated; 500 if upstream Scrub::geoFirewallCreate fails. Caveat: country_code is an internal numeric ID, not ISO-3166. Siblings: scrubIpsDeleteGeoRule, createRule, createFilter."
      }
    },
    "/scrub_ips/{id}/delete_geo_rule": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "title": "Delete Geo Firewall Rule",
                "description": "Delete geo firewall rule for your scrub ip",
                "required": [
                  "rule_id"
                ],
                "type": "object",
                "properties": {
                  "rule_id": {
                    "type": "integer",
                    "example": 2045
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "ScrubIp ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/DeleteFirewallResponse"
          },
          "400": {
            "$ref": "#/components/responses/DeleteFirewall400Err"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/DeleteFirewall500Err"
          }
        },
        "operationId": "scrubIpsDeleteGeoRule",
        "summary": "Delete a geo firewall rule by rule_id from getScrubIpDetails",
        "description": "Removes a previously created geographic firewall rule from the Scrub IP service. The rule_id must come from the `filter_firewall.geo_rules[].id` array returned by getScrubIpDetails — the endpoint validates the id belongs to this service before deleting. Path param: `id` (integer, required) — Scrub IP service ID. Body (JSON): {`rule_id`: integer, required}. Returns {success: true, text: 'Firewall Rule has been deleted.'}. Errors: 400 Invalid Service, 'Rule Id is required.' or 'Invalid rule id' (rule does not belong to this service); 401 unauthenticated; 500 if upstream Scrub::geoFirewallDelete fails. Caveat: removing a country/ASN block re-admits that traffic. Siblings: createGeoRule, scrubIpsDeleteRule, deleteFilter, getScrubIpDetails."
      }
    },
    "/scrub_ips/{id}/create_filter": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFilter"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "ScrubIp ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "201": {
            "$ref": "#/components/responses/CreateFilterResponse"
          },
          "400": {
            "$ref": "#/components/responses/CreateFilter400Err"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/CreateFilter500Err"
          }
        },
        "operationId": "createFilter",
        "summary": "Apply a predefined scrubbing filter (DNS/HTTP/synproxy) to a port",
        "description": "Attaches a named scrubbing profile to a destination port on the protected IP, applying protocol-aware mitigation (DNS amplification protection, HTTP rate limiting, synproxy SYN-cookies). Call getScrubIpFilterTypes first to list valid `filter_type` values. Path param: `id` (integer, required) — service ID. Body (CreateFilter): `filter_type` (string, required, one of getScrubIpFilterTypes keys), `port` (int, required, >= 0). Destination IP is locked to the service IP server-side; synproxy uses a different shape internally. Returns 201 {success: true, text: 'New filter has been created.'}. Errors: 400 'Filter type is empty/invalid', 'Port is invalid', or Invalid Service; 401 unauthenticated; 500 if upstream Scrub::filterCreate fails. Siblings: deleteFilter, getScrubIpFilterTypes, createRule."
      }
    },
    "/scrub_ips/{id}/delete_filter": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateFilter"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "ScrubIp ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/DeleteFilterResponse"
          },
          "400": {
            "$ref": "#/components/responses/DeleteFilter400Err"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/DeleteFilter500Err"
          }
        },
        "operationId": "deleteFilter",
        "summary": "Remove a scrubbing filter by matching filter_type and port",
        "description": "Removes a previously attached scrubbing profile from the protected IP. Identification is by composite key, not `rule_id` — pass the same `filter_type` and `port` that were used in `createFilter`. The endpoint splits `filter_type` on `_` to dispatch to the correct delete shape (synproxy vs generic). Sibling ops: `createFilter`, `getScrubIpFilterTypes`.\n\n**Path:** `id` (integer, required) — Scrub IP service ID.\n\n**Body fields:**\n- `filter_type` (string, required) — must match an enabled type from `getScrubIpFilterTypes`.\n- `port` (integer, required) — must be `> 0`.\n\n**Returns:** `{ success: true, text: 'Filter is deleted.' }`.\n\n**Errors:**\n- `400` — `'Filter is required.'` / `'Port is required.'` / `'Invalid filter'` / `Invalid Service`.\n- `401` — unauthenticated.\n- `500` — upstream `Scrub::filterDelete` failed.\n\n**Caveat:** the port loses its protocol-specific scrubbing protection until `createFilter` is called again with the same composite key.\n"
      }
    },
    "/scrub_ips/order": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/OrderScrubGetResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getOrderDetail",
        "summary": "Get plans, pricing, and eligible IPs for a new Scrub IP order",
        "description": "Returns the data needed to render a new-order form: `packageCosts` (default services_id and recurring price in customer currency with symbol), `serviceTypes` (each buyable plan with services_id, services_name, services_cost, services_module), and `ips` (the customer's existing VPS/server/floating IPs eligible to be put behind a scrubber, each with service_id, service_module, service_hostname). Use as a precursor to putScrubIps (validate) or placeScrubOrder (commit). No path/query/body parameters. Returns object. Errors: 401 unauthenticated. Caveat: ips list is filtered to the session account; pricing is converted to the customer's currency. Siblings: putScrubIps, placeScrubOrder, getScrubIpsList."
      },
      "put": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrubIpPlaceOrder"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "description": "Scrub IP order validation result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "continue": {
                      "type": "boolean"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "serviceType": {
                      "type": "integer"
                    },
                    "serviceCost": {
                      "type": "number"
                    },
                    "originalCost": {
                      "type": "number"
                    },
                    "repeatServiceCost": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putScrubIps",
        "summary": "Validate a Scrub IP order and return effective pricing without billing",
        "description": "Dry-runs a Scrub IP purchase via validate_buy_scrub_ip and returns whether the order would succeed plus the resolved pricing — without creating an invoice. Use to render a real-time price/error panel as the user picks options. No path parameters. Body (ScrubIpPlaceOrder): `serviceType` (services_id from getOrderDetail.serviceTypes), `ip` (one of getOrderDetail.ips), optional `coupon`. Returns {continue: bool, errors: [], serviceType, serviceCost, originalCost, repeatServiceCost}. Errors: 401 unauthenticated; validation failures appear in `errors`, not as HTTP 4xx. Caveat: idempotent — call as often as needed; 422 on invalid coupon surfaces in the errors array. Siblings: getOrderDetail, placeScrubOrder, getScrubIpsList."
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScrubIpPlaceOrder"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "201": {
            "$ref": "#/components/responses/ScrubIpPlaceOrder"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a DDoS-mitigated scrub IP. Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "placeScrubOrder",
        "summary": "Place a new Scrub IP DDoS protection order and generate an invoice",
        "description": "Commits the order: re-runs validate_buy_scrub_ip then place_buy_scrub_ip which creates the service row, repeat_invoice, and a one-time invoice for the prorated charge. Use putScrubIps first to surface errors without billing. No path parameters. Body (ScrubIpPlaceOrder): `serviceType` (services_id), `ip` (eligible IP from getOrderDetail). Returns 201 {success: true, text: 'ScrubIp order is placed.', order_details: {total_cost, service_id, invoice_id, invoice_description, cj_params}}. Errors: 400 {success: false, text: 'Unable to place order.', errors: []} on validation; 401 unauthenticated; 422 on invalid serviceType/ip; 409 if the IP is already protected. Caveat: invoice is unpaid at creation — pay via Pay endpoints to activate. Siblings: putScrubIps, getOrderDetail, enableScrub, getScrubIpInvoices."
      }
    },
    "/scrub_ips/{id}/logs": {
      "get": {
        "tags": [
          "Scrub Ips"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ScrubIpLogs"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getScrubIpLogs",
        "summary": "Get last 50000 packet/event log entries for the protected IP",
        "description": "Pulls scrubbing telemetry directly from the SCRUBLOGS clickhouse-style backend: timestamp, source IP, target IP, target port, protocol (ICMP/IGMP/TCP/UDP/etc.), byte_count, action (Allow/Drop/Challenge), and the matching filter label. Use for incident analysis, validating new firewall rules, or proving a DDoS attack hit the scrubber. Path param: `id` (string, required) — service ID. No body/query parameters. Timestamps are converted to the customer's timezone. Returns array of log rows (ScrubIpsLogRowSchema), most recent first, capped at 50000. Errors: 401 unauthenticated; returns false if id is not owned or upstream returns no data — not a 404. Caveat: large response; logs are not real-time and source IPs are reverse-byte-ordered. Siblings: getScrubIpDetails, enableScrub, createRule."
      },
      "parameters": [
        {
          "examples": {
            "ScrubOrderIdExample": {
              "value": "413232  "
            }
          },
          "name": "id",
          "description": "Scrub Order ID",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/servers": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ServerRow"
                  }
                }
              }
            },
            "links": {
              "GetServerDetails": {
                "operationId": "getServerInfo",
                "parameters": {
                  "id": "$response.body#/0/server_id"
                },
                "description": "Use the `server_id` from any item in the response array to fetch full server details."
              }
            },
            "description": "The listing of `Servers` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getServerList",
        "summary": "List all dedicated servers owned by the authenticated customer",
        "description": "Use to enumerate physical bare-metal dedicated servers on the calling account. No params, no body. Filters `servers` by session `account_id`.\nReturns: array of `{ server_id, account_lid, server_hostname, server_status }`. Use `server_id` with `getServerInfo` for full hardware/network/IPMI details, `getServerInvoices` for billing, or `serverIpmiPowerGet` for chassis power state. Errors: 401 if not authenticated; empty array if account owns no servers.\nSibling ops: `getServerInfo` (details), `getVpsList` (virtual instead of physical hardware), `getMPServers` (purchasable inventory, not owned). For IPMI status across many servers in one call, prefer `serverBulkIpmiPowerGet`."
      }
    },
    "/servers/order": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerOrder"
                }
              }
            },
            "description": "Server Ordering details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewServer",
        "summary": "Get custom dedicated server ordering options, regions, and pricing",
        "description": "Use before placing a fully custom (non-Rapid-Deploy) dedicated server order to discover available CPUs, drives, memory tiers, OS images, control panels, RAID levels, bandwidth packages, IP blocks, and regions with monthly prices. No params, no body.\nReturns: object with `config_li` keyed by category (`cpu_li`, `hd_li`, `memory_li`, `bandwidth_li`, `ips_li`, `os_li`, `cp_li`, `raid_li`) plus `regions`. Use returned IDs as POST values for `addServer`. Note `hd_li` and `memory_li` are nested by `cpu` id — the chosen CPU constrains valid drive/memory options. Errors: 401 if not authenticated.\nSibling ops: `addServer` (commits the order), `buyItNowServerOrder` (pre-built marketplace alternative), `getMPServers` (browse marketplace)."
      },
      "post": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "text": {
                      "description": "Status message.",
                      "type": "string",
                      "example": "Order Completed"
                    },
                    "invoice": {
                      "description": "Invoice ID for payment.",
                      "type": "integer"
                    },
                    "order": {
                      "description": "Server order ID.",
                      "type": "integer"
                    }
                  }
                }
              }
            },
            "links": {
              "GetServerInvoice": {
                "operationId": "getBillingInvoice",
                "parameters": {
                  "id": "$response.body#/invoice"
                },
                "description": "Use the invoice ID to view invoice details or proceed to payment."
              },
              "ViewNewServer": {
                "operationId": "getServerInfo",
                "parameters": {
                  "id": "$response.body#/order"
                },
                "description": "Use the order ID to view the newly created server."
              }
            },
            "description": "Server order placed successfully."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a dedicated or rapid-deploy server. Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addServer",
        "summary": "Place a custom dedicated server order, creating a real billable invoice",
        "description": "Submits a fully custom dedicated server order. Creates a `pending` `servers` row, a `Repeat_Invoice`, and the first invoice, then emails customer + admin. Caveat: real billable order — confirm with the user first.\nBody (form fields): `cpu` (id from `cpu_li`), `hd[]` (array of drive ids), `memory`, `bandwidth`, `ips`, `os`, `cp`, `raid` (ids from `getNewServer`), `region` (region_id), `servername` (valid hostname), `rootpass`, `tos` (must be true), optional `comment`. `account.server_order_discount` (if set) applies.\nReturns: `{ text:'Order Completed', invoice, order }`. Errors: 422 'Missing/Invalid <field>'; 401 unauth.\nSibling ops: `getNewServer` (options), `placeBuyNowServer` (pre-built path), `getServerInfo` (view new order), `getServerInvoices`."
      }
    },
    "/servers/{id}": {
      "get": {
        "tags": [
          "Servers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "Server ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Server"
                }
              }
            },
            "links": {
              "GetServerInvoices": {
                "operationId": "getServerInvoices",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Retrieve billing invoices for this server."
              },
              "ServerIpmiLive": {
                "operationId": "serverIpmiLiveGet",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "View IPMI live connection information for this server."
              }
            },
            "description": "Server details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getServerInfo",
        "summary": "Get full hardware, network, and lifecycle details for a dedicated server",
        "description": "Use to fetch complete configuration for one dedicated server — hardware, network/VLAN/IP layout, asset assignments, location, status, billing references, and client action links. Path param: `id` (integer server_id, from `getServerList`). No body.\nReturns: `ViewServer::getDetails()` shape: `serviceInfo`, `networkInfo` (vlans + assets, with `ipmi_admin_username`/`ipmi_admin_password` and admin lease creds REDACTED for client safety), normalized `client_links`, `serviceType`. `admin_links`/raw `settings`/`csrf` stripped. Errors: 404 not owned; 401 unauth.\nSibling ops: `getServerInvoices`, `serverIpmiLiveGet`, `serverIpmiPowerGet` (single — prefer `serverBulkIpmiPowerGet` for many), `getServerReverseDns`, `getServersWelcomeEmail`, `serversCancel`."
      },
      "post": {
        "tags": [
          "Servers"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "Server ID number.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateServerInfo",
        "summary": "Update settings on a dedicated server order (shares handler with view)",
        "description": "Use to modify metadata on an existing dedicated server order. Path param: `id` (integer server_id). Currently this method shares the same handler as `getServerInfo` (`View::go()`) — no dedicated update fields are processed; treat it as deprecated/no-op pending field-specific endpoints. For hostname, password, or rDNS changes use the dedicated ops below.\nReturns: same payload shape as `getServerInfo`. Errors: 404 if `id` not owned by caller; 401 unauth.\nSibling ops: prefer `postServerReverseDns` (rDNS), `serverIpmiPowerPost` (power), `serverIpmiLivePost` (IPMI access), `serversCancel` (cancel). For new orders use `addServer` or `placeBuyNowServer`. View-only: `getServerInfo`."
      },
      "delete": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServerCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "serversCancel",
        "summary": "Cancel a dedicated server service at the end of the current billing cycle",
        "description": "Submits a cancellation request for a dedicated server. The server is deprovisioned and recurring billing stops at the end of the current billing cycle (not an immediate refund). Path param: `id` (integer server_id, from `getServerList`). No body.\nCaveat: billing-affecting action — always confirm with the user. Hardware-attached data may be wiped on deprovisioning. Returns: `{ success:bool, text:'Servers is canceled.' }`. Errors: 404 if `id` not owned by caller; 409 if already cancelled or non-active; 401 unauth.\nSibling ops: `getServerInfo` (current status), `getServerInvoices` (outstanding charges), VPS counterpart `VPSCancel`. To re-order after cancel use `addServer` or `placeBuyNowServer`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "Server ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/servers/{id}/invoices": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getServerInvoices",
        "summary": "List billing invoices (charges + payments) tied to one dedicated server",
        "description": "Use to retrieve the invoice history for a single dedicated server — e.g. before a cancel, refund, or to show outstanding balances. Path param: `id` (integer server_id from `getServerList`). No body. Inherits from `MyAdmin\\Api\\Billing\\InvoicesList` with module=servers.\nReturns: `ChargeInvoiceRows` array — invoice rows with id, date, amount, status, currency, line items. Errors: 404 if `id` not owned by the caller; 401 unauth.\nSibling ops: `getServerInfo` (current service state), `serversCancel` (cancel), `getBillingInvoice` (single invoice by invoice id), `getVpsInvoices`/`getDomainInvoices` for other modules, `getServersWelcomeEmail` to resend setup info."
      },
      "parameters": [
        {
          "name": "id",
          "description": "Server ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/servers/{id}/ipmi_live": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServerIpmiGetResponse"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "serverIpmiLiveGet",
        "summary": "Read current IPMI Live whitelist + KVM gateway URL for a dedicated server",
        "description": "Reads the active IPMI Live session for a dedicated server — the temporary whitelisted public IP, the customer-side IPMI gateway URL, and the IPMI client (read-only) credentials so the customer can open the KVM/console. Looks up the asset's IPMI IP, the location's IPMI group, and any active `ipmi_ips` lease (3-hour TTL). Sibling ops: `serverIpmiLivePost` (allocate whitelist slot), `serverIpmiPowerGet` / `serverIpmiPowerPost` (chassis power).\n\n**Path:** `id` (integer, required) — server_id from `getServerList`.\n\n**Body / query:** None. Optionally pass `asset` (asset_id) to target a specific asset; default is first asset.\n\n**Returns:** when an active lease exists `{ text (html), public_ip, allowed_ip, client_username, client_password }`. When no lease yet: `{ text: 'Setup not yet completed' }` — then call `serverIpmiLivePost` to allocate a slot.\n\n**Auth:** Session/API key. Ownership enforced via `server_custid`.\n\n**Errors:**\n- `404` — `id` not owned, or `asset` not on this server.\n- `409` — service not `active`.\n- `200` with error text `'No IPMI IP Set'` / `'Invalid IPMI IP'` / `'Live IPMI not Available for this location.'` when the asset/location is not configured for IPMI Live.\n\n**Caveat:** returns `client_password` — never log/echo verbatim.\n\n**Related calls:**\n- **Allocate:** `serverIpmiLivePost`.\n- **Chassis power:** `serverIpmiPowerGet`, `serverIpmiPowerPost`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ServerIpmiLiveRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServerIpmiLiveRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServerIpmiGetResponse"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "serverIpmiLivePost",
        "summary": "Whitelist an IP for IPMI Live KVM gateway access (3-hour lease)",
        "description": "Allocates / refreshes an IPMI Live whitelist slot so the customer's specified IP can reach the BMC's KVM/console for 3 hours. Picks a free `ipmi_ips` row for the location's `ipmi_group`, refreshes the lease if the same IP is already allocated, otherwise pushes the new whitelist via `ipmi_live_setup()`. Sibling ops: `serverIpmiLiveGet` (read current lease), `serverIpmiPowerPost` (DESTRUCTIVE — chassis power).\n\n**Path:** `id` (integer, required) — server_id.\n\n**Body fields:**\n- `ip` (string, required) — public IPv4 to whitelist.\n- `asset` (integer, optional) — asset_id; defaults to first asset on the server.\n\n**Returns:** `{ text (html), public_ip, allowed_ip, client_username, client_password }` for KVM login.\n\n**Auth:** Session/API key. Ownership enforced via `server_custid`.\n\n**Errors:**\n- `404` — `id` not owned, or `asset` not on this server.\n- `409` — service not `active`.\n- `200` with error text — `'An Invalid IP was passed.'`, `'No Live IPs are currently free for use with the IPMI Gateway. Please wait <duration> for the next IP to free up.'`, `'There was an error communicating with the IPMI Management server'`, `'No IPMI IP Set'` / `'Invalid IPMI IP'` / `'Live IPMI not Available for this location.'`.\n\n**Caveat:** returns IPMI client password — handle securely; whitelist exposes the BMC briefly.\n\n**Related calls:**\n- **Read current lease:** `serverIpmiLiveGet`.\n- **Power control:** `serverIpmiPowerPost`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "Server ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/servers/{id}/welcome_email": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getServersWelcomeEmail",
        "summary": "Resend the dedicated server welcome email with setup credentials",
        "description": "Use when the customer asks for the original setup/login info to be re-sent (root password, IPs, control-panel URL). Path param: `id` (integer server_id, must be `active`). No body. Invokes `server_welcome_email($id)` which re-sends the welcome message to the account's email.\nReturns: `{ text:'Welcome Email has been resent.' }`. Errors: 404 if `id` not owned by caller; 409 if service not active (cancelled/pending/suspended); 401 unauth. Caveat: re-sending is rate-sensitive; do not call repeatedly in a loop. The email may contain root credentials — confirm intent before triggering.\nSibling ops: `getServerInfo` (status check), `getServerInvoices`, `getVpsWelcomeEmail` for VPS, `getDomainsWelcomeEmail` for domains."
      },
      "parameters": [
        {
          "name": "id",
          "description": "Server ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/signup": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginSubmissionExample"
              },
              "examples": {
                "LoginSubmissionExampleExample": {
                  "value": {
                    "login": "user@domain.com",
                    "passwd": "mypassword",
                    "remember": "true",
                    "g-recaptcha-response": {
                      "__v_isShallow": false,
                      "dep": {
                        "w": 0,
                        "n": 0
                      },
                      "__v_isRef": true,
                      "_rawValue": "zzzzz",
                      "_value": "zzzzz"
                    }
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "description": "Account created successfully."
          },
          "402": {
            "$ref": "#/components/responses/LoginResponseError"
          }
        },
        "security": [
          {}
        ],
        "operationId": "submitSignup",
        "summary": "Create a new customer account (email + password + captcha + ToS)",
        "description": "First step of the signup flow before adding payment or services. Public — no auth required. The account is created in `pending` state and moved to `active` once the email-confirmation code is verified; an `account.activated` event then fires (welcome email + admin notification). MaxMind GeoIP populates `country` from the client IP. Sibling ops: `submitLogin`, `getCaptcha`, `getLoginInfo`, `addBillingPrepay`, plus the `add*` service ops to follow up after signup.\n\n**Body fields** (JSON or form):\n- `login` (string, required) — email; must be valid and not an alias like `+tag` or dotted gmail.\n- `passwd` (string, required) — 4–64 chars.\n- `tos` (truthy, required) — `yes` / `true` / `1`.\n- `captcha` (string, required) — answer to the phrase from `getCaptcha` or `getLoginInfo` (server reads the phrase from `$_SESSION['captchaSignup']`).\n- `email_confirmation` (string, conditional) — 8-char code emailed on the first attempt; server returns `400 { field: 'email_confirmation' }` until provided.\n- `remember` (boolean / `'true'` / `'yes'` / `'1'`, optional) — 256-day cookie.\n\n**Returns:** `{ sessionId, account_id, account_lid, ima }`.\n\n**Errors:**\n- `400` — missing or invalid `login` / `passwd` / `tos` / `captcha` / `email_confirmation`; blocked-domain or aliased-email; account already exists.\n- `402` per `LoginResponseError` — signup gate misconfigured upstream.\n\n**Related calls:**\n- **Prerequisite:** `getCaptcha` or `getLoginInfo`.\n- **After signup:** `submitLogin`, `addBillingPrepay`, plus any `add*` order op.\n"
      }
    },
    "/ssl": {
      "get": {
        "tags": [
          "SSL-Certificates"
        ],
        "responses": {
          "200": {
            "links": {
              "GetSslDetails": {
                "operationId": "getSslInfo",
                "description": "Use the SSL certificate ID from any item in the response array to fetch full certificate details."
              }
            },
            "description": "The listing of `SSL` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "getSslList",
        "summary": "List all SSL certificates on the authenticated customer account with status and hostname",
        "description": "Use to enumerate every SSL certificate (DV/OV/EV) the current customer owns before drilling into a specific cert. Returns an array of SslRow objects with id, hostname, services_name (package), status (pending/active/expired/canceled), and company. No query parameters - results are auto-scoped to the session account_id. Empty array if customer has no certs. Returns 401 if unauthenticated. Pair the returned id with getSslInfo for full details, getSslInvoices for billing, getSslWelcomeEmail to resend credentials, sslCancel to terminate, or addSsl to order a new cert. Status values may be stale relative to CA - issuance/validation can take minutes to hours after order.\n\nSibling ops: `getSslInfo`, `getNewSsl` (catalog), `addSsl` (order new cert)."
      }
    },
    "/ssl/order": {
      "get": {
        "tags": [
          "SSL-Certificates"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "description": "SSL certificate types and pricing.",
                  "type": "object"
                }
              }
            },
            "description": "Available SSL certificate types and pricing for ordering."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewSsl",
        "summary": "Get available SSL certificate packages and pricing for placing a new order",
        "description": "Use before addSsl to discover which DV/OV/EV certificate types and validation tiers are buyable, plus their costs. Returns object with packageCosts (services_id keyed map of float costs) and serviceTypes (full list of SSL product offerings from the get_service_types event). No parameters required - prices are in the customer's currency. Returns 401 if unauthenticated. Show these to the customer to pick a service_type, then call putSsl to dry-run validation (hostname, CSR, coupon) without charging, then addSsl to commit. Costs do not include taxes or applied coupons — putSsl returns the actual computed price with discounts.\n\nSibling ops: `putSsl` (validate), `addSsl` (commit), `getSslList` (existing certs), `getSslInfo` (per-cert)."
      },
      "put": {
        "tags": [
          "SSL-Certificates"
        ],
        "responses": {
          "200": {
            "description": "Validate SSL Order response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putSsl",
        "summary": "Validate an SSL certificate order without charging - dry-run before addSsl",
        "description": "Use after getNewSsl and before addSsl to verify hostname, CSR, service_type, frequency, and coupon_code are acceptable without creating an invoice or charging the customer. Body params (form): frequency (months, default 12), service_type, hostname, csr, coupon_code, plus extra/vars per cert type. Returns continue (bool), errors (array), serviceType, serviceCost (after coupon), originalCost, hostname, couponCode. If continue=false the errors array explains what to fix - typical issues are invalid hostname/CSR mismatch, expired coupon, or unsupported service_type. Returns 401 if unauthenticated, 422 on validation failure semantics. No state is mutated. Always run this before addSsl to prevent failed charges.\nSibling ops: `getNewSsl` (catalog), `addSsl` (commit)."
      },
      "post": {
        "tags": [
          "SSL-Certificates"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order an SSL certificate (DV/OV/EV). Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addSsl",
        "summary": "Place a new SSL certificate order - creates invoice and queues issuance",
        "description": "[DESTRUCTIVE] Use after putSsl returns continue=true to commit the SSL order. Body (form): frequency (default 12 months), service_type, hostname, csr, coupon_code, plus per-type vars/extra. Re-runs validate_buy_ssl then calls place_buy_ssl which creates the service row, generates invoice (iid/iids/real_iids), and returns serviceId, serviceCost, invoice_description. CA validation is async - issuance takes minutes to hours and may require DNS or email validation post-order. If validation fails, returns continue=false with errors and no charge. Returns 401 unauthenticated, 422 invalid input. Caveat: cert is not active until invoice paid AND CA validation completes. Poll status via getSslInfo; resend instructions via getSslWelcomeEmail.\n\nSibling ops: `getNewSsl` (catalog), `putSsl` (validate), `getSslInfo` (poll), `getSslInvoices`, `initiatePayment` (settle invoice), `getSslWelcomeEmail`, `sslCancel`."
      }
    },
    "/ssl/{id}": {
      "get": {
        "tags": [
          "SSL-Certificates"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "SSL certificate ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "description": "SSL certificate service details.",
                  "type": "object"
                }
              }
            },
            "description": "Detailed SSL certificate information."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getSslInfo",
        "summary": "Get full details for one SSL certificate by id - status, expiration, links",
        "description": "Use to inspect a single SSL cert after locating its id via getSslList. Path param id (integer, required) is the ssl_id; cross-account ids return 404 (get_service enforces ownership). Returns the ViewSSL detail payload: hostname, service_type, status, expiration, company, plus client_links (rewrite/reissue/install actions available to the customer). admin_links, settings, csrf are stripped from client responses. Returns 401 unauthenticated, 404 if id not owned by the session customer. Reissue/rekey/install actions surfaced in client_links are time-sensitive and may require fresh DNS validation. Pair with getSslInvoices for billing history, getSslWelcomeEmail to resend, sslCancel to terminate, updateSslInfo to modify settings.\n\nSibling ops: `updateSslInfo`, `getSslInvoices`, `getSslWelcomeEmail`, `sslCancel`, `getSslList`."
      },
      "post": {
        "tags": [
          "SSL-Certificates"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "SSL certificate ID number.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateSslInfo",
        "summary": "Update mutable settings on an existing SSL certificate order by id",
        "description": "Use to modify mutable fields on a customer-owned SSL cert (e.g. contact info, renewal preferences, hostname or CSR data depending on cert state and CA rules). Path param id (string/int, required) is the ssl_id. Body params depend on the cert package and which fields the underlying service supports - inspect getSslInfo client_links first to see which actions are exposed. Returns SuccessTextResponse on success. Returns 401 unauthenticated, 404 if id not owned, 409 if cert state forbids the change (e.g. canceled or pending CA validation), 422 on invalid field values. Caveat: changes that affect the certificate identity (hostname, CSR) typically trigger a reissue with the CA which is time-sensitive and may require new DNS or email validation.\n\nSibling ops: `getSslInfo` (read), `sslCancel` (terminate), `getSslWelcomeEmail`."
      },
      "delete": {
        "tags": [
          "SSL-Certificates"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SSLCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "sslCancel",
        "summary": "Cancel an SSL certificate service - stops renewals at end of billing cycle",
        "description": "[DESTRUCTIVE] Use to cancel a customer-owned SSL cert. Path param id (integer, required) is the ssl_id. Cancellation marks the service for non-renewal - the cert stays valid until its current paid period ends, after which auto-billing stops. The CA-issued certificate itself is NOT revoked by this call (file a separate revocation request if needed). Returns SSLCancelResponse with success bool and text. Returns 401 unauthenticated, 404 if id not owned by session customer, error if the cancel_service hook fails. Caveat: irreversible at the billing level - re-enabling requires a new addSsl order. Verify the right cert with getSslInfo and confirm no unpaid charges via getSslInvoices first.\n\nSibling ops: `getSslInfo` (verify cert), `getSslInvoices` (check unpaid), `addSsl` (re-order)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "SSL Cert ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/ssl/{id}/invoices": {
      "get": {
        "tags": [
          "SSL-Certificates"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getSslInvoices",
        "summary": "List all billing invoices and charges tied to one SSL certificate by id",
        "description": "Use to retrieve the full invoice history for a single SSL cert - initial order, renewals, and any addon charges. Path param id (integer, required) is the ssl_id; ownership is enforced via get_service so cross-account ids return an Invalid Service error. Returns ChargeInvoiceRows: success bool plus invoices array of charge/invoice rows with iid, date, cost, status (paid/unpaid/refunded), and description. Returns 401 unauthenticated, 400 if the id resolves to no service. Useful for auditing renewals before sslCancel, reconciling payment failures, or showing the customer their billing history.\n\nSibling ops: `getSslInfo`, `sslCancel`, `getSslWelcomeEmail`, `getBillingInvoice` (per-invoice detail), `initiatePayment` (settle unpaid)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "SSL Cert ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/ssl/{id}/welcome_email": {
      "get": {
        "tags": [
          "SSL-Certificates"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getSslWelcomeEmail",
        "summary": "Resend the SSL welcome email with cert credentials and install instructions",
        "description": "Use when a customer lost the original welcome email containing CSR submission steps, validation links, or installation guidance for an active SSL cert. Path param id (integer, required) is the ssl_id. Triggers the module's ssl_welcome_email function to re-send to the account's email on file. Returns SuccessTextResponse: text='Welcome Email has been resent.' Returns 401 unauthenticated, 404 if id not found or not owned by session customer ('Invalid Service Passed'), 409 if cert status is not 'active' (pending/canceled/expired certs do not have a welcome email to resend). Caveat: cannot change the destination email - update the account profile first if the customer's address has changed.\n\nSibling ops: `getSslInfo` (verify status), `sslCancel` (terminate), `updateAccountInfo` (change email first)."
      },
      "parameters": [
        {
          "name": "id",
          "description": "SSL Cert ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/tickets": {
      "get": {
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "name": "page",
            "description": "Page number for paginated results.",
            "schema": {
              "default": 1,
              "type": "integer"
            },
            "in": "query"
          },
          {
            "name": "period",
            "description": "How far back to show tickets from. Value is in days.",
            "schema": {
              "default": "30",
              "enum": [
                "30",
                "90",
                "365",
                "1825",
                "all"
              ],
              "type": "string"
            },
            "in": "query"
          },
          {
            "name": "view",
            "description": "The status of tickets to view. Possible values are Open, Closed, On Hold, and In Progress.  If not specified it will show all types.",
            "schema": {
              "enum": [
                "Open",
                "Closed",
                "On Hold",
                "In Progress"
              ],
              "type": "string"
            },
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tickets"
                },
                "examples": {
                  "TicketsExample": {
                    "value": {
                      "ima": "client",
                      "custid": "223513",
                      "view": "Open",
                      "currentPage": 1,
                      "limit": 50,
                      "sortcol": 6,
                      "sortdir": 1,
                      "rowsOffset": 0,
                      "tickets": [],
                      "pages": 7,
                      "rowsTotal": 311,
                      "inboxCount": 311,
                      "countArray": {
                        "Open": 3,
                        "On Hold": 3,
                        "Closed": 305
                      },
                      "viewText": "Inbox"
                    }
                  }
                }
              }
            },
            "links": {
              "GetTicketDetails": {
                "operationId": "getTicketInfo",
                "description": "Use a ticket ID from the response to fetch full ticket details and reply history."
              }
            },
            "description": "The listing of support tickets."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getTicketsList",
        "summary": "List the authenticated account's support tickets with status and date filters",
        "description": "Use to browse the customer's helpdesk tickets, paginated, with optional status and recency filters. Returns tickets where email matches the session account_lid. Query params: page (int, default 1, 50 per page), period (string: '30', '90', '365', '1825', or 'all' days back; default '30'), view (string: 'Open', 'Closed', 'On Hold', 'In Progress'; omit for all). Body: none. Returns: object with tickets[], total, pages, currentPage, st_count[] (counts grouped by status: Open/On Hold/Closed), selected_period, view. Errors: 401 unauthorized session. Note ticketstatusid mapping (Open=4, On Hold=5, Closed=6, In Progress=7). To search by subject/email/mask use postTicketsList. To open a ticket detail use getTicketInfo with the returned id. To create a new ticket see addNewTicket.\n\nSibling ops: `getTicketInfo` (detail), `postTicketsList` (search), `addNewTicket` (open new)."
      },
      "post": {
        "tags": [
          "Tickets"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tickets"
                }
              }
            },
            "description": "Search through the ticket system for a given email, subject, or ticket mask id."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postTicketsList",
        "summary": "Search the authenticated account's tickets by subject, email, or mask ID",
        "description": "Use when the user supplies a search term (subject keyword, email substring, or full ticket mask ID like 'ABC-123-456'). Scoped to tickets owned by the session account_lid. Body (form): search (string, required). If the term contains exactly two hyphens it is treated as an exact ticketmaskid match; otherwise a LIKE search runs across subject, email, and ticketmaskid. Returns: array of up to 25 matching ticket rows ordered by lastactivity DESC, each enriched with lastactivity_time (human-relative). Errors: 400 if search is empty or missing; 401 unauthorized. Caveat: this is a POST that reads, not a creator. To create see addNewTicket. To paginate full inbox use getTicketsList. To open one use getTicketInfo.\n\nSibling ops: `getTicketsList` (full inbox), `getTicketInfo` (detail), `addNewTicket` (open new)."
      }
    },
    "/tickets/new": {
      "get": {
        "tags": [
          "Tickets"
        ],
        "responses": {
          "200": {
            "description": "New ticket form data including departments and service categories."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewTicket",
        "summary": "Fetch services and product options to populate the new-ticket form",
        "description": "Use to populate dropdowns before calling addNewTicket. Returns the customer's services grouped by product type so the user can attach a ticket to a specific resource. Iterates all enabled modules (vps, webhosting, domains, mail, etc.; mailbaby instances see only mail) and filters out services with status canceled, deleted, or fraud. Params: none. Body: none. Returns: object keyed by product TITLE (e.g. 'Vps', 'Webhosting'), each value a map of '{module}-{service_id}' to a description string including title, type/plan, VPS hypervisor name where applicable, and uppercase status tag. Errors: 401 unauthorized. Use the returned product key as the 'product' field on addNewTicket.\n\nSibling ops: `addNewTicket` (consumes the product key), `getTicketsList`."
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TicketNew"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TicketNew"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Tickets"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketNewResponse"
                }
              }
            },
            "links": {
              "ViewCreatedTicket": {
                "operationId": "getTicketInfo",
                "parameters": {
                  "id": "$response.body#/ticket_id"
                },
                "description": "Use the ticket ID from the response to view the newly created ticket details."
              }
            },
            "description": "A successful response after creating a ticket."
          },
          "400": {
            "$ref": "#/components/responses/TicketNewResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "addNewTicket",
        "summary": "Open a new helpdesk ticket, optionally linked to a service and attachments",
        "description": "Use when the customer wants to contact support. Creates the Kayako ticket in the 'New Unassigned' department (id 18). Body (form): subject (string, required), body (string, required), product (string, optional, format '{module}-{service_id}' from getNewTicket), service_id+service_module (alternative to product), attachments[] (optional, each {name, type, content base64}), and optional server-access custom fields ip, root_pass, sudo_user, sudo_pass, port_no, server_access (passwords are AES-encrypted with a generated auth_key). Returns: {success: true, text, ticket: ticketmaskid}. Errors: 400 missing subject or body; 401 unauthorized; 422 ticket creation failure. Sibling: getNewTicket for product list, getTicketInfo to view, ReplyTicket to add replies."
      }
    },
    "/tickets/{id}": {
      "get": {
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "Ticket ID number.",
            "schema": {
              "type": "number"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ViewTicketResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "getTicketInfo",
        "summary": "Get full ticket details including subject, status, and the reply thread",
        "description": "Use to render a ticket page or feed full context to an LLM. Path: id (int, ticket ID, e.g. 1511222). Returns ticket header (subject, status, department, dates), the ordered post/reply history, attachments, and any custom-field values. Resolved via Ticket::getTicket(id, account_lid) so cross-account access returns Invalid ticket. Body: none. Errors: 401 unauthorized; 404/422 'Invalid ticket!' when the id is unknown or owned by another account. Caveats: the same path with POST appends a reply (postTicketInfo) and DELETE closes the ticket (deleteTicketInfo) — it does not destroy data. Siblings: ReplyTicket, updateTicketInfo, CloseTicket, getTicketsList."
      },
      "put": {
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "Ticket ID number.",
            "schema": {
              "type": "number"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ViewTicketResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "putTicketInfo",
        "summary": "Update a ticket's properties such as subject or status (stub, not implemented)",
        "description": "Reserved for future use to update ticket subject/status. The PHP handler is currently an empty stub that returns no body, so callers should not rely on it in production. Path: id (int). Body: would carry subject/status fields when implemented. Returns: undefined behavior today. Errors: 401 unauthorized; expect 404/422 when implemented if id is invalid or not owned. Caveats: prefer updateTicketInfo (POST /tickets/{id}/update) for editing custom-field values today, postTicketInfo to add a reply, CloseTicket or deleteTicketInfo to close. Avoid scripting against this endpoint until the handler ships. Siblings: getTicketInfo, ReplyTicket."
      },
      "post": {
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "Ticket ID number.",
            "schema": {
              "type": "number"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ViewTicketResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "postTicketInfo",
        "summary": "Append a reply (and optional attachment, server-access fields) to a ticket",
        "description": "Use to post a customer reply on an existing ticket. Path: id (int ticket ID). Body: body (string reply text; trimmed to first 500 words), file_attachment (multipart upload, optional), and the server-access custom fields ip/root_pass/sudo_user/sudo_pass/port_no/server_access (passwords AES-encrypted with auth_key=7). Either body OR an attachment is required. Notifies any swticketwatchers staff via templated email after posting. Returns: {status: 'success', message: 'Reply posted successfully'}. Errors: 400 'Please enter a message or attach a file'; 401 unauthorized; 404/422 'Invalid ticket!' when id missing or cross-account. Sibling: ReplyTicket (cleaner JSON-only reply at /tickets/{id}/reply), updateTicketInfo, getTicketInfo, deleteTicketInfo."
      },
      "delete": {
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "Ticket ID number.",
            "schema": {
              "type": "number"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ViewTicketResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "deleteTicketInfo",
        "summary": "Close a customer ticket via DELETE verb (closes only, never destroys data)",
        "description": "Use when the customer marks a ticket resolved. IMPORTANT: despite the DELETE verb this only CLOSES the ticket via Ticket::closeTicket — no data is destroyed. Closed tickets remain readable through getTicketInfo and appear in getTicketsList when view=Closed. Path: id (int ticket ID). Body: none. Returns: 'Ticket is closed!' string on success. Errors: 401 unauthorized; 404/422 'Invalid ticket!' when id is unknown or owned by another account. Idempotent on already-closed tickets. Siblings: CloseTicket (GET /tickets/{id}/close — same effect, simpler URL), ReplyTicket to add a final reply before closing, getTicketInfo to verify state."
      }
    },
    "/tickets/{id}/update": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTicket"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTicket"
              }
            }
          }
        },
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The ticket ID number.",
            "schema": {
              "type": "number"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/UpdateTicketResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "updateTicketInfo",
        "summary": "Update a ticket's custom field values (server-access details, etc.)",
        "description": "Use to save or change the structured custom-field values attached to a ticket — typically server-access details supplied by the customer. Path: id (int ticket ID). Body (form): one field per custom-field title, lowercased with spaces replaced by underscores (e.g. ip, root_pass, sudo_user, sudo_pass, port_no, server_access). Field id 7 (auth_key) is skipped — never set it directly. Returns: {success: true, text: 'Ticket is updated!'} or {success: false, text: 'Unable to update ticket'}. Errors: 401 unauthorized; 404 invalid or non-owned ticket. Caveats: this updates metadata only — to add a reply use ReplyTicket, to close use CloseTicket, to read current state use getTicketInfo.\n\nSibling ops: `getTicketInfo` (read), `ReplyTicket` (reply), `CloseTicket` (close)."
      }
    },
    "/tickets/{id}/reply": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReplyTicketRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ReplyTicketRequest"
              }
            }
          }
        },
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The ticket ID number.",
            "schema": {
              "type": "number"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ReplyTicketResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "ReplyTicket",
        "summary": "Post a simple text reply to an existing ticket thread (no attachments)",
        "description": "Use this lightweight endpoint to add a reply to an existing ticket without attachments or server-access fields. Cleaner alternative to postTicketInfo when only text is being submitted. Path: id (int ticket ID). Body (form): content (string, required reply body). Returns: {success: true, post_id: int} on success or {success: false, text: 'Reply content cannot be empty!' | 'Unable to reply ticket'}. Errors: 401 unauthorized; 404 implied via 'Unable to reply ticket' when id is invalid or owned by another account. Siblings: postTicketInfo (POST /tickets/{id}, supports attachments + custom fields), updateTicketInfo (custom fields only), CloseTicket, getTicketInfo to verify the new post_id appears in the thread."
      }
    },
    "/tickets/{id}/close": {
      "get": {
        "tags": [
          "Tickets"
        ],
        "parameters": [
          {
            "examples": {
              "TicketIdExample": {
                "value": "1511222"
              }
            },
            "name": "id",
            "description": "Ticket ID",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/CloseTicketResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "CloseTicket",
        "summary": "Close an open support ticket via simple GET request (no body required)",
        "description": "Use to close a ticket from a link or one-click action — closure-only equivalent of deleteTicketInfo with friendlier semantics. Calls Ticket::closeTicket on the resolved ticket and leaves the record fully readable; closed tickets disappear from the active inbox but remain in getTicketsList when view=Closed. Path: id (int ticket ID, e.g. 1511222). Body: none. Returns: {success: true, text: 'Ticket is closed!'} or {success: false, text: 'Unable to close ticket'}. Errors: 401 unauthorized; 404 implied via 'Unable to close ticket' when id is unknown or cross-account. Idempotent on already-closed tickets. Siblings: deleteTicketInfo (DELETE /tickets/{id} — same effect), getTicketInfo to confirm new status."
      }
    },
    "/vps": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/VpsRow"
                  }
                }
              }
            },
            "links": {
              "GetVpsDetails": {
                "operationId": "getVpsInfo",
                "parameters": {
                  "id": "$response.body#/0/vps_id"
                },
                "description": "Use the `vps_id` from any item in the response array to fetch full VPS details."
              }
            },
            "description": "The listing of `Vps` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsList",
        "summary": "List all VPS services on the customer's account",
        "description": "Enumerates every VPS owned by the authenticated customer — status, hostname, primary IP, plan name, and monthly cost. The canonical entry point for finding a VPS `id` to pass into other VPS operations (`getVpsInfo`, lifecycle, billing, backups, etc.). No path params, no query params, no body. Server-side filtered by session account; rows come from the `vps` table joined to `repeat_invoices` (for cost) and `services` (for plan name). Returns an array of `VpsRow` (empty array if the account has no VPS). Sibling ops: `getVpsInfo` (full detail), `getNewVps`/`putVps`/`addVps` (order a new one).\n\n**Returned fields (per row):**\n- `vps_id` (integer) — canonical VPS id, used in every `/vps/{id}/*` path.\n- `vps_name` (string) — display name shown in the dashboard.\n- `vps_hostname` (string) — FQDN currently assigned to the VPS.\n- `vps_ip` (string) — primary IPv4 address.\n- `vps_status` (string enum) — `active`, `pending` (awaiting payment/provisioning), `suspended` (non-payment), or `cancelled`.\n- `services_name` (string) — service-type name (e.g. `KVM`, `KVM Storage`, `HyperV`).\n- `repeat_invoices_cost` (decimal) — current monthly cost in the VPS's billing currency.\n- `vps_comment` (string|null) — customer-provided note.\n\n**Auth:** Session (`sessionid` header) or API key (`X-API-KEY` header). API key preferred for integrations.\n\n**Errors:**\n- `401 Unauthorized` — missing/invalid session or API key.\n\n**Related calls:**\n- **Next (per-VPS):** `getVpsInfo` (full detail incl. extra IPs, slices, addons), `getVpsInvoices` (billing per VPS), `doVpsRestart`/`doVpsStart`/`doVpsStop` (lifecycle).\n- **Order a new VPS:** `getNewVps` (catalog) → `putVps` (validate + quote) → `addVps` (place + invoice).\n- **Cancel:** `VPSCancel` (end of cycle, customer-initiated).\n"
      }
    },
    "/vps/order": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpsOrder"
                }
              }
            },
            "description": "VPS Order information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewVps",
        "summary": "Get the VPS order catalog — platforms, OS templates, locations, pricing",
        "description": "Step 1 of the VPS order flow. Returns the full ordering catalog the customer needs to build a valid VPS configuration: virtualization platforms (`kvm`, `kvmstorage`, `hyperv`), OS templates grouped by platform+distro, datacenter locations with current stock flags, per-slice resource costs converted to the customer's billing currency, control-panel prices, and slice resource defaults (RAM/HD/BW per slice, max slices per VPS). No path or query params, no body. The response drives the order form; once the user picks a config, call `putVps` for a dry-run price quote, then `addVps` to actually place the order. Sibling ops: `putVps`, `addVps`, `getVpsList` (existing VPS).\n\n**Returned top-level fields** (schema `VpsOrder`):\n- `platformNames` (object) — display names keyed by platform tag (`{kvm: \"KVM\", kvmstorage: \"KVM Storage\", hyperv: \"HyperV\"}`).\n- `platformPackages` (object) — service-type ids keyed by platform tag (`{kvm: 32, kvmstorage: 57, hyperv: 54}`).\n- `packageCosts` (object) — base list cost keyed by service-type id.\n- `templates` (object) — nested `{platform: {os: {template_file: template_version}}}` template tree.\n- `osNames` (object) — display name per `template_os` key.\n- `locationNames` (object) — `{1: \"New Jersey\", 2: \"Los Angeles\", 3: \"Dallas, TX\"}`.\n- `locationStock` (object) — `{location_id: {platform_tag: bool}}` — `true` = in stock.\n- `vpsSlice<Platform>Cost` (float) — per-slice cost per platform in customer currency (e.g. `vpsSliceKvmLCost`, `vpsSliceKvmStorageCost`, `vpsSliceHypervCost`, `vpsSliceOvzCost`).\n- `cpanelCost`, `daCost` (float) — control-panel addon costs.\n- `ramSlice`, `hdSlice`, `bwSlice` (int) — RAM (MB), HD (GB), BW (GB) per slice.\n- `maxSlices` (int) — `VPS_SLICE_MAX` cap for non-admin callers.\n- `currency`, `currencySymbol` (string) — derived from the account profile.\n\n**Auth:** Session or API key.\n\n**Errors:**\n- `401 Unauthorized` — missing session/API key.\n\n**Related calls:**\n- **Next:** `putVps` (validate + quote a chosen config — no charge), `addVps` (place the order).\n- **After ordering:** pay via `initiatePayment` with the returned `real_iids`, then poll `getVpsInfo` until `vps_status == \"active\"`.\n\n**Example happy-path response (abridged):**\n```json\n{\n  \"platformNames\": {\"kvm\": \"KVM\", \"kvmstorage\": \"KVM Storage\", \"hyperv\": \"HyperV\"},\n  \"platformPackages\": {\"kvm\": 32, \"kvmstorage\": 57, \"hyperv\": 54},\n  \"locationNames\": {\"1\": \"New Jersey\", \"2\": \"Los Angeles\", \"3\": \"Dallas, TX\"},\n  \"locationStock\": {\"1\": {\"kvm\": true, \"kvmstorage\": true, \"hyperv\": false}},\n  \"osNames\": {\"centos-7-x86_64\": \"CentOS 7\", \"ubuntu-22.04-x86_64\": \"Ubuntu 22.04\"},\n  \"templates\": {\"kvm\": {\"centos-7-x86_64\": {\"centos-7-x86_64.qcow2\": \"7\"}}},\n  \"vpsSliceKvmLCost\": 6.00,\n  \"cpanelCost\": 18.00,\n  \"ramSlice\": 2048, \"hdSlice\": 25, \"bwSlice\": 2000, \"maxSlices\": 8,\n  \"currency\": \"USD\", \"currencySymbol\": \"$\"\n}\n```\n"
      },
      "put": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VpsOrderPutRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/VpsOrderPutRequest"
              }
            }
          }
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpsOrderPutResponse"
                }
              }
            },
            "description": "Validate VPS order response."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putVps",
        "summary": "Validate a VPS order configuration and quote the cost — dry run, no charge",
        "description": "Step 2 of the VPS order flow. Validates a chosen VPS configuration (platform, OS, slices, location, control panel, coupon, etc.) against stock and policy, applies any coupon discount and frequency discount, and returns a cost breakdown — without creating any invoice or service record. Use this to preview the first-month and recurring cost before the customer commits via `addVps`. The body shape is identical between `putVps` and `addVps`; the only difference is the HTTP verb — PUT validates, POST commits. Sibling ops: `getNewVps` (catalog), `addVps` (place order).\n\n**Required body fields:**\n- `osDistro` (string) — OS template tag from `getNewVps.osNames` (e.g. `centos-7-x86_64`).\n- `osVersion` (string) — OS version from `getNewVps.templates[platform][os][template_file]`.\n- `vpsPlatform` (string) — one of the keys in `getNewVps.platformNames`: `kvm`, `kvmstorage`, `hyperv`, `openvz`, `ssdopenvz`, `virtuozzo`, `ssdvirtuozzo`, `lxc`, `cloudkvm`, `docker`. HTML stripped server-side.\n- `slices` (integer) — `1 ≤ slices ≤ getNewVps.maxSlices`. Windows (`kvm` with `osDistro` starting `windows` or `os==5`, plus `hyperv`/`cloudkvm` Windows) requires `slices ≥ 2`.\n\n**Optional body fields:**\n- `location` (integer, default 1) — `1`=NJ, `2`=LA, `3`=TX. Out-of-stock platforms in a location auto-fail with an error.\n- `period` (integer, default 1) — billing cycle in months: `1` / `6` / `12` / `24` / `36`. Discounts: 6mo=5%, 12mo=10%, 24mo=15%, 36mo=20%.\n- `controlpanel` (string, default `none`) — `none` / `cpanel` (forces CentOS) / `da` (DirectAdmin). Incompatible with Windows.\n- `coupon` (string) — coupon code; validated against `coupons` table (custid match, module=`vps`, applies-to-service-type, usable count). Returns \"Invalid Coupon\" if not found/usable.\n- `hostname` (string) — FQDN matching `/^.*\\..*\\..*$/` (e.g. `server.example.com`). Skipped for Windows KVM (auto-set to `vps{id}` server-side).\n- `rootpass` (string) — required for all Linux platforms. Must match `/(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*\\W)/`. Not required for Windows.\n- `comment` (string) — free-form note saved on the `vps` row.\n\n**Returned fields** (schema `VpsOrderPutResponse`):\n- `continue` (bool) — `true` if validation passed and the order can be POSTed. If `false`, render `errors` and do not call `addVps`.\n- `errors` (array of strings) — human-readable validation messages.\n- `coupon_code` (integer) — id of the matched coupon row, or `0` if no coupon applied.\n- `service_cost` (float) — first-period cost in customer currency (includes coupon + period discount).\n- `slice_cost` (float) — per-slice cost after coupon.\n- `repeat_service_cost` (float) — recurring monthly/period cost after coupon and period discount.\n- `original_slice_cost`, `original_cost` (float) — undiscounted reference values.\n- `service_type` (integer) — resolved service-type id (e.g. KVM Linux=33, KVM Win=32, KVM Storage=57, HyperV=54, OpenVZ=31, Virtuozzo=55).\n- `monthly_service_cost` (float) — recurring cost normalized to monthly.\n\n**Side effects:** None — `PUT /vps/order` is a pure read.\n\n**Errors:**\n- `400` — missing required field (`osDistro` / `vpsPlatform` / `slices` / `osVersion`) or invalid `location`. Body shape: `{error: \"Missing field <name>\"}`.\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Prerequisite:** `getNewVps` (provides every option value referenced in the body).\n- **Next:** `addVps` (place the order with the same body once `continue == true`).\n\n**Example request body:**\n```json\n{\n  \"vpsPlatform\": \"kvm\",\n  \"osDistro\": \"centos-7-x86_64\",\n  \"osVersion\": \"centos-7-x86_64.qcow2\",\n  \"slices\": 2,\n  \"location\": 1,\n  \"period\": 1,\n  \"controlpanel\": \"none\",\n  \"hostname\": \"web1.example.com\",\n  \"rootpass\": \"Sup3rS3cret!\",\n  \"coupon\": \"\"\n}\n```\n**Example response (validation passed):**\n```json\n{\n  \"continue\": true,\n  \"errors\": [],\n  \"coupon_code\": 0,\n  \"service_cost\": 12.00,\n  \"slice_cost\": 6.00,\n  \"repeat_service_cost\": 12.00,\n  \"service_type\": 33,\n  \"monthly_service_cost\": 12.00,\n  \"platform\": \"kvm\", \"os\": \"centos-7-x86_64\",\n  \"slices\": 2, \"location\": 1, \"period\": 1,\n  \"hostname\": \"web1.example.com\"\n}\n```\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VpsOrderPostRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/VpsOrderPostRequest"
              }
            }
          }
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a VPS instance. Returns invoice IDs for /billing/pay/{method}/{invoices}.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addVps",
        "summary": "Place a new VPS order, create the invoice, and queue provisioning",
        "description": "Step 3 of the VPS order flow — actually places the order. Revalidates the same configuration that `putVps` accepts (so the request is safe even if `putVps` was skipped), then calls `place_buy_vps`: allocates a backing hypervisor server via `get_vps_next_server`, creates a `Repeat_Invoice` ORM row for the recurring charge, generates the initial `invoices` row via `$repeat_invoice->invoice()`, inserts a `vps` service record with `vps_status='pending'`, and creates any control-panel addon invoices (CPanel/DirectAdmin). Returns the new service id plus invoice ids the caller must pay before provisioning runs. **Real money** — call `putVps` first to preview cost. Sibling ops: `getNewVps`, `putVps`, `getVpsInfo`, `VPSCancel`.\n\n**Body fields:** Identical to `putVps`. Required: `osDistro`, `osVersion`, `vpsPlatform`, `slices`. Optional: `location` (default 1), `period` (default 1), `coupon`, `hostname`, `rootpass`, `controlpanel` (default `none`), `comment`. Same validation rules apply (slice range, rootpass regex for Linux, hostname FQDN format, platform↔OS↔controlpanel compatibility).\n\n**Returned fields** (schema `ServiceOrderPostResponse`):\n- `success` (bool) — `true` on successful placement.\n- `serviceid` (integer) — new VPS id; use this with `getVpsInfo` to poll status.\n- `iid` (string) — primary invoice id (numeric).\n- `real_iids` (array of strings) — numeric invoice ids to pass to `initiatePayment` (`invoices` path param).\n- `iids` (array of strings) — tagged invoice ids (e.g. `SERVICEvps12345`) — alternative payment identifier.\n- `total_cost` (decimal string) — total to pay across all generated invoices.\n- `invoice_description` (string) — human-readable summary (e.g. `KVM 2 Slices`).\n- `cj_params` (object) — Commission Junction tracking parameters (affiliate flows).\n\n**Side effects:**\n- Inserts row into `vps` table (`vps_status='pending'`).\n- Inserts `repeat_invoices` row for the recurring charge.\n- Inserts `invoices` row for the first period charge.\n- Inserts additional `invoices` rows for CPanel/DirectAdmin addons if `controlpanel != 'none'`.\n- Logs a `vps` signup event in `history_log`.\n- Saves root password to `history_log` (encrypted at rest).\n\n**Errors:**\n- `400 Bad Request` — validation failed; response body is the `errors` array from validation.\n- `401 Unauthorized` — missing session/API key.\n\n**Related calls:**\n- **Prerequisite:** `getNewVps` (catalog), `putVps` (preview cost — strongly recommended).\n- **Next:** `getBillingInvoice` (review invoice line items), `initiatePayment` (`GET /billing/pay/{method}/{invoices}` — pay with `real_iids`), then `getVpsInfo` (poll for `vps_status == \"active\"`), `getVpsWelcomeEmail` (resend credentials).\n- **Cancel before paying:** `VPSCancel`.\n\n**Example request body:** Same as `putVps`.\n\n**Example response:**\n```json\n{\n  \"success\": true,\n  \"serviceid\": 12345,\n  \"iid\": \"25296600\",\n  \"real_iids\": [\"25296600\"],\n  \"iids\": [\"SERVICEvps12345\"],\n  \"total_cost\": \"12.00\",\n  \"invoice_description\": \"KVM 2 Slices\",\n  \"cj_params\": {}\n}\n```\n**Full ordering happy path:**\n```text\nGET /vps/order                                  -> catalog (getNewVps)\nPUT /vps/order { ...config }                    -> price quote (putVps)\nPOST /vps/order { ...config }                   -> { serviceid, real_iids } (addVps)\nGET /billing/invoices/{iid}                     -> confirm invoice (getBillingInvoice)\nGET /billing/pay/cc/{real_iids[0]}              -> pay (payInvoice; type=submit|redirect|single)\nGET /vps/{serviceid}                            -> poll until vps_status==\"active\" (getVpsInfo)\n```\n"
      }
    },
    "/vps/{id}": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vps"
                }
              }
            },
            "links": {
              "GetVpsInvoices": {
                "operationId": "getVpsInvoices",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Retrieve billing invoices for this VPS."
              },
              "RestartVps": {
                "operationId": "doVpsRestart",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Restart this VPS."
              },
              "StartVps": {
                "operationId": "doVpsStart",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Power on this VPS."
              },
              "StopVps": {
                "operationId": "doVpsStop",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Power off this VPS."
              },
              "BackupVps": {
                "operationId": "postVpsBackup",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Create a backup of this VPS."
              },
              "ListVpsBackups": {
                "operationId": "getVpsBackups",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "List available backups for this VPS."
              },
              "ReinstallVpsOS": {
                "operationId": "getVpsReinstallOs",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "View available OS templates for reinstalling this VPS."
              },
              "GetVpsReverseDns": {
                "operationId": "getVpsReverseDns",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "View reverse DNS entries for this VPS."
              }
            },
            "description": "The VPS Information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsInfo",
        "summary": "Get full details for one VPS — IPs, hostname, OS, slices, status, addons",
        "description": "Returns everything the customer dashboard shows for a single VPS — hostname, primary IP plus any extra IPs, OS, allocated slices (CPU/RAM/disk), current `vps_status`, plan/service-type, datacenter location, billing currency, and `serviceAddons` (extra IPs and additional GB disk). Read-only. Backed by `ViewVPS::getDetails()`; ownership is enforced via `get_service($id, 'vps')` — cross-customer requests return 404. Use to render a VPS detail page, to verify ownership before mutating, or to poll `vps_status` after `addVps` (status flips `pending` → `active` once provisioning completes). Sibling ops: `getVpsList`, `doVpsRestart`/`doVpsStart`/`doVpsStop` (lifecycle), `getVpsTrafficUsage` (bandwidth), `getVpsBackups`, `getVpsInvoices`, `updateVpsInfo`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Returned fields** (schema `Vps` plus extras):\n- Core: `vps_id`, `vps_hostname`, `vps_ip`, `vps_status`, `vps_slices`, `vps_os`, `vps_type` (service-type id), `vps_server` (backing hypervisor id), `vps_custid`, `vps_comment`, `vps_coupon`.\n- `services_name` (string) — plan name (e.g. `KVM`, `HyperV`).\n- `client_links` (array) — UI action links (`{name, link, icon}`) for restart, snapshot, console, etc. Internal `?link=queue&action=...` URLs are pre-resolved.\n- `serviceAddons` (object) — `{extra_ips: [...], additional_gb: <int>}` populated from `repeat_invoices` rows that match `Additional IP*` / `Additional N GB Space*` patterns.\n- **Stripped fields:** `admin_links`, `settings`, `csrf` are removed before response.\n\n**Auth:** Session or API key. Customer must own the VPS (enforced via `vps_custid` match).\n\n**Errors:**\n- `401 Unauthorized` — missing session/API key.\n- `404 Invalid VPS Passed` — `id` does not exist or is owned by a different account.\n\n**Related calls:**\n- **Lifecycle:** `doVpsStart`, `doVpsStop`, `doVpsRestart`.\n- **Maintenance:** `postVpsChangeHostname`, `postVpsChangeRootPassword`, `postVpsReverseDns`.\n- **Upgrade:** `getVpsSlices`/`postVpsSlices`, `getVpsBuyHdSpace`/`putVpsBuyHdSpace`/`postVpsBuyHdSpace`, `getVpsBuyIp`/`postVpsBuyIp`.\n- **Backups:** `getVpsBackup` (create), `getVpsBackups` (list), `downloadVpsBackup`, `postVpsRestore`.\n- **Billing:** `getVpsInvoices`, `VPSCancel`.\n- **Metrics:** `getVpsTrafficUsage`.\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateVpsInfo",
        "summary": "Update editable settings on a VPS service record",
        "description": "Write-back endpoint for the VPS service record — typically the dashboard's \"save changes\" action. The body is processed by `ViewVPS::getDetails()`, the same handler as the GET, so the accepted fields mirror what the VPS detail view edits in place (e.g. customer comment/label, display preferences). For lifecycle or provisioning changes use the dedicated endpoints — they enforce platform-specific validation and queue hypervisor actions correctly. Sibling ops: `getVpsInfo`, `VPSCancel`, and all the dedicated mutation endpoints listed below.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** Form-encoded or JSON with editable fields (handler decides which are persisted). Most callers should use the dedicated endpoints instead.\n\n**Returns:** `SuccessTextResponse` — `{text: \"...\"}` on success.\n\n**Auth:** Session/API key. Ownership enforced via `vps_custid` match.\n\n**Errors:**\n- `401 Unauthorized` — missing session/API key.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n\n**Prefer these dedicated endpoints when applicable:**\n- Hostname → `postVpsChangeHostname` (OpenVZ/Virtuozzo only).\n- Root password → `postVpsChangeRootPassword` (specific value) or `postVpsResetPassword` (random).\n- Reverse DNS → `postVpsReverseDns`.\n- Slice upgrade/downgrade → `getVpsSlices` → `postVpsSlices` (creates prorated invoice).\n- Additional disk → `getVpsBuyHdSpace` → `putVpsBuyHdSpace` → `postVpsBuyHdSpace`.\n- Additional IPs → `getVpsBuyIp` → `postVpsBuyIp`.\n- Timezone → `getVpsChangeTimezone` → `postVpsChangeTimezone`.\n- Cancel service entirely → `VPSCancel`.\n"
      },
      "delete": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/VPSCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "VPSCancel",
        "summary": "Cancel a VPS service at the end of the current billing cycle",
        "description": "Customer-facing cancel — schedules termination, stops future renewals, and queues deprovisioning. Billing continues until the end of the already-paid period, and the customer keeps access until then; this endpoint does **not** issue refunds. Delegates to `Billing\\CancelService::go()` (shared cancellation handler used across all service modules). The repeat-invoice is marked for cancellation and the VPS row's status will eventually flip to `cancelled`. Reversible: a customer can typically un-cancel before the cycle ends by opening a support ticket. Sibling ops: `getVpsInfo` (verify status), `getVpsInvoices` (review final invoices), `getVpsBackup` (snapshot before cancellation).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `VPSCancelResponse` — confirmation text plus the scheduled cancellation date.\n\n**Side effects:**\n- Sets the `repeat_invoices` row for the VPS to non-renewing.\n- Logs the cancellation event in `history_log`.\n- Queues `vpsqueue` deprovisioning action to run at end-of-cycle.\n- Does NOT immediately stop the VPS — power state is unchanged until the cycle ends.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401 Unauthorized` — missing session/API key.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n\n**Related calls:**\n- **Before cancelling:** `getVpsBackup` (create a final snapshot), `downloadVpsBackup` (export it).\n- **After cancelling:** `getVpsInfo` (confirm `vps_status` flipped), `getVpsInvoices` (final invoices).\n- **Sibling cancels on other modules:** `CancelDomain`, `mailCancel`, `webhostingCancel`, etc. all use the same `CancelService` handler.\n"
      }
    },
    "/vps/{id}/backup": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsBackup",
        "summary": "Trigger a manual on-demand snapshot/backup of the VPS",
        "description": "Creates an on-demand backup of the VPS — typically called before a risky change (OS reinstall, slice upgrade, restore from older backup). Enqueues a `backup` action on the hypervisor (`history_log` `vpsqueue` entry) and returns immediately; the actual snapshot runs asynchronously and may take a few minutes. Despite being GET, this is a side-effecting action and the MCP parser flags it accordingly. The new backup, once complete, appears in `getVpsBackups` keyed by `name`. Sibling ops: `getVpsBackups` (list), `downloadVpsBackup` (download via pre-signed URL), `deleteVpsBackup`, `postVpsRestore`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text: \"Action has been sent to the server. Please allow up to 2 minutes for action to be completed.\", queueId: <integer> }` — `queueId` is the row id in `queue_log`/`history_log` and can be used to track action status.\n\n**Backup limits (per platform):**\n- KVM / KVM Storage: backups **enabled**.\n- HyperV, OpenVZ, SSD-OpenVZ, Virtuozzo, SSD-Virtuozzo: **disabled** server-side (returns 400 \"Backups are disabled for this type\").\n- Max 4 backups per VPS for non-admin callers. If at the cap, returns \"Currently 4 backups per VPS max\" — delete an old one first via `deleteVpsBackup`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `409 VPS is not active` — `vps_status != \"active\"`.\n- `400 Backups are disabled for this type` — incompatible platform.\n- `400 Currently 4 backups per VPS max` — at cap.\n\n**Related calls:**\n- **List existing:** `getVpsBackups`.\n- **Download:** `downloadVpsBackup` (PATCH; returns 24-hr pre-signed URL for MinIO backups; Swift/ZFS disabled).\n- **Delete:** `deleteVpsBackup` (DELETE; Swift/MinIO only).\n- **Restore from a backup:** `postVpsRestore`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/backups": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpsBackupRows"
                }
              }
            },
            "description": "The listing of the available backups"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsBackups",
        "summary": "List existing backups for the VPS across Swift, MinIO, and ZFS",
        "description": "Enumerates the backup files available for the VPS across all backend storage systems (OpenStack Swift, MinIO/S3, and ZFS snapshots). Each entry's `name` is the canonical identifier the caller must pass to sibling endpoints (`downloadVpsBackup`, `deleteVpsBackup`, `postVpsRestore`) — there is no separate integer id. The list is filtered to the VPS's owner by default; admins can list all backups on the account by passing `all=1`. Sibling ops: `getVpsBackup` (create new), `downloadVpsBackup`, `deleteVpsBackup`, `postVpsRestore`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Query params:**\n- `all` (string, optional, enum `0`/`1`, default `0`) — set to `1` to list every backup across all services on the account, not just the ones for `{id}`.\n\n**Returns:** `VpsBackupRows` — array of objects:\n- `name` (string) — canonical identifier, e.g. `vps-12345-2026-05-12.tar.gz`.\n- `type` (string enum) — `swift` / `minio` / `zfs`. Determines which operations are available (see Sibling notes).\n- `service` (integer) — VPS id the backup belongs to.\n- `path` (string) — storage path/URL.\n- `size` (integer) — bytes.\n- `repoIdx` (integer) — repository index (0 or 1 for Swift; selects which credentials/bucket).\n- `extra` (array, optional) — multi-part backup pieces.\n\n**Auth:** Session/API key. Ownership enforced via `vps_custid` on the parent VPS.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n\n**Capability matrix by backup type:**\n- `swift`: list ✓, download ✗ (disabled — contact support), delete ✓.\n- `minio`: list ✓, download ✓ (24-hr pre-signed URL via `downloadVpsBackup`), delete ✓.\n- `zfs`: list ✓, download ✗ (disabled), delete ✗ (open a ticket).\n\n**Related calls:**\n- **Create new:** `getVpsBackup`.\n- **Download:** `downloadVpsBackup` (PATCH).\n- **Delete:** `deleteVpsBackup` (DELETE).\n- **Restore:** `postVpsRestore` — pass `backup` as `<type>:<service>:<name>`.\n"
      },
      "delete": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "file",
            "description": "The backup filename to delete.",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteVpsBackup",
        "summary": "Permanently delete a VPS backup file by name (irreversible)",
        "description": "Removes a backup file from storage to free space. For `minio`-typed backups runs `mc rm --force --recursive` on the path; for `swift`-typed backups removes the storage object via the Swift API. ZFS-typed backups **cannot** be deleted through this endpoint — they return an error directing the caller to open a support ticket. **Irreversible** — once deleted the backup cannot be used with `postVpsRestore` or `downloadVpsBackup`. Sibling ops: `getVpsBackups` (list), `downloadVpsBackup` (download first), `getVpsBackup` (create new).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Query params:**\n- `file` (string, required) — exact `name` from `getVpsBackups` (the canonical backup identifier).\n\n**Returns:** `SuccessTextResponse` — `Backup <name> removed.` on success.\n\n**Side effects:**\n- **minio**: `mc rm --force --recursive` removes the entire backup directory.\n- **swift**: deletes the listed object(s) plus any multi-part `extra` segments.\n\n**Auth:** Session/API key. Ownership enforced via parent VPS.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `No file specified` — `file` query param missing.\n- **ZFS backup:** `This type of backup if not removable. Please contact support if you need this removed.`\n- **MinIO rm failure:** `Error removing file <name>`.\n\n**Related calls:**\n- **List first to get `name`:** `getVpsBackups`.\n- **Download before deleting:** `downloadVpsBackup` (MinIO only; Swift/ZFS disabled).\n- **Restore (don't delete):** `postVpsRestore`.\n"
      },
      "patch": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "description": "The backup filename to download.",
                    "type": "string"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "required": [
                  "file"
                ],
                "type": "object",
                "properties": {
                  "file": {
                    "description": "The backup filename to download.",
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "text": {
                      "type": "string"
                    },
                    "url": {
                      "description": "A pre-signed download URL valid for 24 hours.",
                      "type": "string"
                    }
                  }
                }
              }
            },
            "description": "Download URL for the backup file."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "downloadVpsBackup",
        "summary": "Issue a 24-hour pre-signed URL to download a MinIO-backed VPS backup",
        "description": "Generates a time-limited download link for a MinIO/S3-backed VPS backup so the customer can fetch it off-platform. Runs `mc share download --expire=24h` against the resolved backup path and returns the resulting public URL — valid for 24 hours from issue. Only `minio`-typed backups are downloadable; `swift` and `zfs` backups have direct download disabled (returns an error directing the customer to support). Sibling ops: `getVpsBackups` (list to find `name`), `postVpsRestore` (restore in place — no download needed), `deleteVpsBackup`, `getVpsBackup` (create new).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body (JSON or multipart, required):**\n- `file` (string, required) — exact `name` from `getVpsBackups`.\n\n**Returns:**\n- `text` (string) — `URL available for the next 24 hours`.\n- `url` (string) — pre-signed download URL (HTTPS).\n\n**Auth:** Session/API key. Ownership enforced via parent VPS.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `No file specified` — `file` body field missing.\n- **Swift backup:** `Downloads for this type have been disabled. Please contact support if you need this backup.`\n- **ZFS backup:** same disabled message.\n- **MinIO share failure:** `Error sharing file <name>`.\n\n**Related calls:**\n- **Prerequisite:** `getVpsBackups` (find a backup with `type == \"minio\"`).\n- **Alternative:** `postVpsRestore` (restore in place — no download).\n- **Cleanup after download:** `deleteVpsBackup`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        },
        {
          "name": "all",
          "description": "Set to `1` to list all backups across all services, not just the ones for the given VPS.",
          "schema": {
            "enum": [
              "0",
              "1"
            ],
            "type": "string"
          },
          "in": "query"
        }
      ]
    },
    "/vps/{id}/block_smtp": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsBlockSmtp",
        "summary": "Block outbound SMTP (port 25) on the VPS to prevent spam/abuse",
        "description": "Blocks outbound SMTP (port 25) traffic on the VPS at the hypervisor level — typical for cPanel/WHM customers who route through a smart relay, or for VPS that should never emit mail directly. Queues a `block_smtp` action on the `vpsqueue` and triggers a VNC re-setup. Despite being GET, this is a side-effecting action and the MCP parser flags it accordingly. **One-way from the client side:** there is no public unblock endpoint — re-enabling outbound SMTP requires a support ticket so abuse history can be reviewed. Sibling ops: `getVpsInfo` (verify state), `doVpsRestart`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }` — `queueId` references the `queue_log` row; allow up to 2 minutes for the iptables/firewall rule to take effect.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `409 VPS is not active` — `vps_status != \"active\"`.\n\n**Reversibility:** Not client-reversible — open a ticket.\n\n**Related calls:**\n- **Verify state:** `getVpsInfo`.\n- **General lifecycle:** `doVpsStart`, `doVpsStop`, `doVpsRestart`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/buy_hd_space": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Get VPS Buy HD Space information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsBuyHdSpace",
        "summary": "Get current additional disk size and per-GB monthly cost for the VPS",
        "description": "Step 1 of the disk-space addon flow. Returns the current \"Additional N GB Space\" already purchased for the VPS (0 if none) and the per-GB monthly cost in USD — both adjusted for the customer's reseller pricing tier via `get_reseller_price`. Read-only. Use this to populate a disk-upgrade form before calling `putVpsBuyHdSpace` (preview) and `postVpsBuyHdSpace` (commit). For whole-plan upgrades (CPU+RAM+disk together) use the slices flow instead (`getVpsSlices` / `postVpsSlices`). Sibling ops: `putVpsBuyHdSpace`, `postVpsBuyHdSpace`, `postVpsSlices`, `getVpsInfo`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:**\n- `gbCost` (float) — per-GB monthly cost in USD (after reseller discount).\n- `size` (integer) — current additional GB already purchased (0 if none).\n\n**Auth:** Session/API key. Ownership enforced via parent VPS.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- **Pre-condition:** an existing `Additional N GB Space for VPS {id}` repeat-invoice is required for the lookup to find a baseline; if it isn't parseable, the endpoint returns an error asking the customer to contact support. New installs always start at `size: 0`.\n\n**Related calls:**\n- **Next (preview):** `putVpsBuyHdSpace` — returns prorated `diffCost` for a target size.\n- **Next (commit):** `postVpsBuyHdSpace` — creates/updates the addon repeat invoice.\n- **Alternative path (whole plan):** `getVpsSlices` → `postVpsSlices`.\n"
      },
      "put": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Pricing preview for the requested HD space size."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putVpsBuyHdSpace",
        "summary": "Preview cost to set additional VPS disk to a target GB size — dry run",
        "description": "Step 2 of the disk-space addon flow. Dry-run that quotes a new \"Additional N GB Space\" addon at the target size, prorated to the VPS's existing billing cycle. **No invoice is created and no charge happens.** Use to show the customer the immediate prorated `diffCost` and the new recurring `cost` before they commit via `postVpsBuyHdSpace`. Sibling ops: `getVpsBuyHdSpace`, `postVpsBuyHdSpace`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `size` (integer, required) — target additional GB. Range `1 ≤ size ≤ 100`; rejected with \"Invalid Size Specified\" otherwise. Rejected with \"No Change Made, Size The Same\" if it equals current.\n\n**Returns:**\n- `gbCost` (float) — per-GB monthly cost in USD.\n- `curSize` (integer) — currently purchased additional GB.\n- `newSize` (integer) — requested target size.\n- `cost` (float) — new recurring cost (size × gbCost × frequency).\n- `diffCost` (float) — prorated immediate charge for the partial cycle plus the remainder of the period (when frequency > 1).\n- `frequency` (integer) — billing cycle in months (e.g. 1, 6, 12).\n\n**Side effects:** None — pure read.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `Invalid Size Specified` — `size` out of `1..100`.\n- `No Change Made, Size The Same` — `size == curSize`.\n\n**Related calls:**\n- **Prerequisite:** `getVpsBuyHdSpace` (read current state).\n- **Next:** `postVpsBuyHdSpace` (commit; creates invoice when diffCost > 0, queues immediate update otherwise).\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Post Buy HD Space for VPS response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsBuyHdSpace",
        "summary": "Buy or resize the VPS additional-disk addon and create a prorated invoice",
        "description": "Step 3 of the disk-space addon flow — commit. Creates or updates the `Additional N GB Space for VPS {id}` `repeat_invoices` row with the new size and recurring cost, then generates a one-off prorated `invoices` row for the immediate difference. The hypervisor disk-grow action is queued either immediately (`update_hdsize` in `vpsqueue`) when no charge is owed, or after the invoice is paid. When increasing from an existing size, any unpaid prior addon invoice is deleted and any already-paid one is credited against `diffCost`. **Real money** — call `putVpsBuyHdSpace` first to preview. Sibling ops: `getVpsBuyHdSpace`, `putVpsBuyHdSpace`, `postVpsSlices`, `initiatePayment`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `size` (integer, required) — target additional GB. Range `1..100`. Must differ from current.\n\n**Returns:**\n- When immediate charge owed: `{ text: \"Invoice Created, Please Pay This To Activate Extra Space\", invoice: <integer> }` — pass `invoice` to `initiatePayment`.\n- When no charge owed (downgrade/credit): `{ text: \"Repeat Invoice Updated, Server Size Update Queued\" }` — disk grow already queued.\n\n**Side effects:**\n- Inserts or updates `repeat_invoices` row for the addon.\n- Inserts `invoices` row for `diffCost` when > 0.\n- Deletes any unpaid prior addon invoices for the same VPS within the last month.\n- Credits any paid prior invoice against `diffCost`.\n- Queues `update_hdsize` in `vpsqueue` when no payment is owed.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `Invalid Size Specified` — `size` out of `1..100`.\n- `No Change Made, Size The Same`.\n- **Pre-condition:** an existing addon row is required. If not found, the request short-circuits (`go()` returns without acting); use the order flow for the very first addon, or `postVpsSlices` for whole-plan upgrades.\n\n**Related calls:**\n- **Preview first:** `putVpsBuyHdSpace`.\n- **Pay the invoice:** `initiatePayment` (`GET /billing/pay/{method}/{invoices}`).\n- **Whole-plan upgrades:** `postVpsSlices` (bundles disk + RAM + CPU).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/buy_ip": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "VPS Buy IP information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsBuyIp",
        "summary": "Read current additional IPs, cap, and per-IP monthly cost for the VPS",
        "description": "Step 1 of the additional-IP addon flow. Returns the list of extra IPs already on the VPS (each with a `cancel_link` for removing that specific addon), how many more are allowed (`maxIps` = `VPS_MAX_IPS` constant), and the per-IP monthly cost converted to the VPS's billing currency. Read-only. Use to render the \"buy another IP\" form and to enforce the cap before calling `postVpsBuyIp`. Sibling ops: `postVpsBuyIp` (purchase one more), `postVpsReverseDns` (set PTR on the new IP), `getVpsReverseDns`, `getVpsInfo`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:**\n- `ipsDetails` (array) — one entry per existing extra IP. Each entry includes:\n  - `ip` (string) — the IPv4 address (parsed from the repeat-invoice description).\n  - `cancel_link` (string) — relative URL `cancel_addon?module=vps&r=<repeat_invoice_id>` to cancel that specific IP addon.\n  - The underlying `repeat_invoices` / `invoices` columns (description, amount, dates, etc.).\n- `ipCount` (integer) — current count of extra IPs already purchased.\n- `maxIps` (integer) — hard cap (`VPS_MAX_IPS`).\n- `ipCost` (float) — per-IP monthly cost, converted from `VPS_IP_COST` to the VPS's billing currency.\n- `currency` (string) — VPS billing currency code (ISO 4217, e.g. `USD`).\n- `currencySymbol` (string).\n\n**Auth:** Session/API key. Ownership enforced via parent VPS.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n\n**Related calls:**\n- **Next (buy one more):** `postVpsBuyIp` — auto-allocates the next free IP on the same hypervisor.\n- **After activation:** `postVpsReverseDns` (set PTR for the new IP), `getVpsInfo` (verify allocation).\n- **Cancel an existing extra IP:** follow the `cancel_link` URL (renders the cancel-addon page).\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Submit VPS Buy IP form response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsBuyIp",
        "summary": "Purchase one additional IP for the VPS and create the invoice",
        "description": "Step 2 of the additional-IP addon flow — commit. Auto-selects the next free IP on the same hypervisor via `vps_get_next_ip`, creates a `Additional IP for VPS {id}` recurring invoice (`repeat_invoices`), and generates an immediate one-off `invoices` row at the current IP cost. **Real money.** The network-side IP allocation happens once the invoice is paid; the IP is bound to the VPS during the next provisioning sweep. Sibling ops: `getVpsBuyIp` (preview), `initiatePayment`, `getVpsInfo` (verify), `postVpsReverseDns` (set PTR after activation).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None — the next free IP is auto-selected.\n\n**Returns:**\n- `text` (string) — `Ordered Additional IP successfully.`\n- `invoice` (integer) — new invoice id to pay via `initiatePayment`.\n\n**Side effects:**\n- Reserves the next free IP on the VPS's `vps_server` (parked until payment).\n- Inserts `repeat_invoices` row (`Additional IP for VPS {id}`, recurring at `ipCost`, frequency from parent service).\n- Inserts `invoices` row for the immediate one-period charge.\n- Logs the addon creation in `myadmin_log`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `VPS already has the maximum number of IPs allowed. If you require additional IPs please contact support.` — `ipCount >= maxIps` (`VPS_MAX_IPS`).\n- `No available free ips on this server. Please contact support to order additional ips.` — `vps_get_next_ip` returned false.\n\n**Related calls:**\n- **Prerequisite:** `getVpsBuyIp` (capacity check + preview).\n- **Next:** `initiatePayment` with the returned `invoice` id, then `getVpsInfo` to confirm allocation.\n- **Post-activation:** `postVpsReverseDns` to set the PTR for the new IP.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/change_hostname": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Current hostname information for the VPS."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsChangeHostname",
        "summary": "Read the VPS's current hostname before changing it",
        "description": "Step 1 of the hostname-change flow. Returns the hostname currently stored on the `vps` row so the customer can confirm the existing value before submitting a new one. Read-only. **Platform restriction:** hostname changes through `postVpsChangeHostname` are only supported on OpenVZ/SSD-OpenVZ/Virtuozzo/SSD-Virtuozzo; KVM and HyperV require a support ticket — so for those platforms this endpoint is informational only. Sibling ops: `postVpsChangeHostname` (apply new value), `postVpsReverseDns` (PTR for primary IP — auto-updated by `postVpsChangeHostname`).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** Current hostname (object form: `{ hostname: \"<fqdn>\" }`).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `409 VPS is not active` — `vps_status != \"active\"`.\n\n**Related calls:**\n- **Next:** `postVpsChangeHostname` (OpenVZ/Virtuozzo only; auto-updates PTR for the primary IP).\n- **PTR for extra IPs:** `postVpsReverseDns`.\n- **Verify after change:** `getVpsInfo`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/HostnameObject"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HostnameObject"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsChangeHostname",
        "summary": "Rename the VPS hostname (OpenVZ/Virtuozzo only) and auto-set PTR for the primary IP",
        "description": "Renames the VPS — validates the FQDN with `valid_hostname()`, sets the reverse-DNS PTR record for the primary IP (`reverse_dns($vps_ip, $hostname)`), and either updates the `vps_hostname` column directly (if the VPS is still `pending`) or queues a `change_hostname` action on the hypervisor (`vpsqueue`) for active services. **Platform restriction:** rejected unless the VPS runs on OpenVZ, SSD-OpenVZ, Virtuozzo, or SSD-Virtuozzo — KVM/HyperV must open a support ticket. Sibling ops: `getVpsChangeHostname`, `postVpsReverseDns`, `getVpsInfo`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `hostname` (string, required) — new FQDN (e.g. `web1.example.com`). Validated by `valid_hostname()`.\n\n**Returns:**\n- For active services: `{ text, queueId }` — `queueId` references the `queue_log` row. Allow ~2 minutes.\n- For pending services: `{ text: \"Hostname Updated\" }` — applied in place.\n\n**Side effects:**\n- Sets PTR record for `vps_ip` via `reverse_dns()`.\n- Either updates `vps_hostname` directly (pending) or queues `change_hostname` (active) plus logs `change_hostname` history entry with the `old to new` transition.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `Invalid Hostname` — fails `valid_hostname()`.\n- `No change in hostname` — value matches current.\n- `Hostname changing is only enabled on OpenVZ/Virtuozzo Platforms currently. Contact support and we can change it for you.` — wrong platform.\n\n**Related calls:**\n- **Prerequisite:** `getVpsChangeHostname` (read current).\n- **Extra IPs need separate PTR updates:** `postVpsReverseDns`.\n- **Verify:** `getVpsInfo` (look for updated `vps_hostname`).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/change_root_password": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Password requirements and current state."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsChangeRootPassword",
        "summary": "Pre-flight check before changing the VPS root password",
        "description": "Step 1 of the root-password change flow. Validates ownership and active status; the response is a placeholder/policy object the dashboard uses to render the form (current implementation does not yet return a populated policy — it short-circuits after the ownership/status checks). Read-only. Use to confirm the VPS exists and is active before calling `postVpsChangeRootPassword`. For a server-generated random password instead, use `postVpsResetPassword`. Sibling ops: `postVpsChangeRootPassword`, `postVpsResetPassword`, `postVpsChangeWebuzoPassword` (Webuzo control panel).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** Object with password requirements/state. Currently a no-op pre-check; reserved for future policy fields (min length, complexity rules, last-change timestamp).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` — `id` not owned by caller.\n- `409 VPS is not active` — `vps_status != \"active\"`.\n\n**Related calls:**\n- **Next (specific password):** `postVpsChangeRootPassword`.\n- **Random password:** `postVpsResetPassword`.\n- **Webuzo control panel password:** `postVpsChangeWebuzoPassword` (separate credential).\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PasswordRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsChangeRootPassword",
        "summary": "Set a specific new root/Administrator password on the VPS",
        "description": "Sets a specific root password (Administrator on Windows) chosen by the customer. Queues a `change_root` action on the hypervisor (`vpsqueue`) and records the new password in `history_log` as a `change_root_password` entry for operator reference. The password takes effect within ~2 minutes. **Caveat:** there is no rollback — to \"undo\", set another new password. For a server-generated random password instead, use `postVpsResetPassword`. Sibling ops: `getVpsChangeRootPassword`, `postVpsResetPassword`, `postVpsChangeWebuzoPassword`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `password` (string, required) — new root/Administrator password. The endpoint does not enforce a regex here (the hypervisor agent applies platform policy), but `validate_buy_vps`-style strength is strongly recommended: 8+ chars, upper, lower, digit, special.\n\n**Returns:** `{ text, queueId }` — `queueId` tracks the action in `queue_log`.\n\n**Side effects:**\n- Inserts `vpsqueue` `change_root` row.\n- Inserts `history_log` `change_root_password` audit entry storing the new password (operator-readable for support).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `400 Missing field \"password\"`.\n\n**Related calls:**\n- **Pre-flight:** `getVpsChangeRootPassword`.\n- **Random instead:** `postVpsResetPassword`.\n- **Control panel password:** `postVpsChangeWebuzoPassword`.\n- **Verify:** `getVpsInfo` (no field change — verification is operational, not via API).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/change_timezone": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "TimeZones": {
                    "value": [
                      "America/New_York",
                      "America/Nome",
                      "America/Noronha",
                      "America/North_Dakota/Beulah",
                      "America/North_Dakota/Center"
                    ]
                  }
                }
              }
            },
            "description": "VPS Change Timezone info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsChangeTimezone",
        "summary": "List IANA timezones supported by the VPS guest OS",
        "description": "Step 1 of the timezone-change flow. Returns the list of IANA timezone identifiers the VPS accepts (e.g. `America/New_York`, `Europe/London`, `Asia/Tokyo`) — sourced from `/usr/share/zoneinfo/zone.tab` on the MyAdmin host. Use to populate a timezone picker before calling `postVpsChangeTimezone`. Read-only. Sibling op: `postVpsChangeTimezone`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** Array of strings — IANA timezone identifiers, sorted alphabetically.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Next:** `postVpsChangeTimezone` (must pass a value present in this array).\n- **Account-level timezone:** `updateAccountInfo` (sets the default for new VPS).\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TimezoneUpdate"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TimezoneUpdate"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsChangeTimezone",
        "summary": "Set the system timezone on the VPS guest OS",
        "description": "Step 2 of the timezone-change flow — commit. Validates `timezone` against the list from `getVpsChangeTimezone`, then queues a `change_timezone` action on the hypervisor (`vpsqueue`). Action takes effect within ~2 minutes. Sibling op: `getVpsChangeTimezone`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `timezone` (string, required) — IANA identifier; **must** be one of the values returned by `getVpsChangeTimezone` (in-array check enforced server-side).\n\n**Returns:** `{ text, queueId }`.\n\n**Side effects:**\n- Inserts `vpsqueue` `change_timezone` row with the validated value.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `422 Invalid timezone` — value not in the supported list.\n\n**Related calls:**\n- **Prerequisite:** `getVpsChangeTimezone` (the only valid source for `timezone` values).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/change_webuzo_password": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/PasswordRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsChangeWebuzoPassword",
        "summary": "Rotate the Webuzo control panel admin password (re-auth required)",
        "description": "Rotates the admin password on the Webuzo control panel that ships pre-installed on certain VPS templates. Re-authenticates the caller via their MyAdmin account password (`account_passwd` md5 check), then calls the Webuzo SDK (`Webuzo_API::change_password`) to apply the new password, updates the stored credential in `history_log`, and emails the customer a confirmation via the `client/client_email.tpl` template. Used for the control panel only — for the underlying OS root/Administrator password use `postVpsChangeRootPassword`/`postVpsResetPassword`. Sibling ops: `postVpsChangeRootPassword`, `postVpsResetPassword`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields (both required):**\n- `password` (string, required) — new Webuzo admin password. Validated by `valid_password()`.\n- `login_password` (string, required) — the customer's current MyAdmin account password (re-auth check; md5-compared to `accounts.account_passwd`).\n\n**Returns:** `{ text }` — `Password updated successfully!`\n\n**Side effects:**\n- Calls Webuzo API to apply new password.\n- Updates the `Webuzo Details` row in `history_log` with the new value.\n- Sends a confirmation email to the account's billing email.\n- Logs the rotation in `myadmin_log`.\n\n**Auth:** Session/API key plus re-auth via `login_password`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `Missing Password` / `Missing Login Password` — body field absent.\n- `Login Password is incorrect!` — `login_password` doesn't match stored hash.\n- `New Password is not valid` — fails `valid_password()`.\n- `Missing Existing Webuzo Password Details.` — no Webuzo credential in `history_log` (contact support).\n- `Unable to update password. Please contact support team for further assistance.` — Webuzo API call failed.\n\n**Related calls:**\n- **OS root password instead:** `postVpsChangeRootPassword`, `postVpsResetPassword`.\n- **Account password rotation:** `updateAccountPassword`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/disable_cd": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsDisableCd",
        "summary": "Remove the virtual CD/DVD device entirely from the VPS configuration",
        "description": "Removes the virtual CD/DVD device from the VPS hardware configuration entirely — distinct from `doVpsEjectCd`, which only unmounts the ISO but leaves the drive attached. Queues a `disable_cd` action on the hypervisor (`vpsqueue`) and triggers a VNC re-setup. Side-effecting GET. Reversible by attaching a new CD via `postVpsInsertCd`. Sibling ops: `doVpsEjectCd` (eject ISO but keep drive), `getVpsInsertCd` (list ISOs), `postVpsInsertCd` (mount ISO).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes for hypervisor action.\n\n**Side effects:**\n- Inserts `vpsqueue` `disable_cd` row.\n- Calls `vps_resetup_vnc()` to refresh the VNC config.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Re-attach drive:** `postVpsInsertCd` (provide ISO URL).\n- **Just unmount the ISO:** `doVpsEjectCd`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/disable_quota": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsDisableQuota",
        "summary": "Disable per-user disk quota enforcement inside the VPS guest OS",
        "description": "Stops enforcing per-user disk quotas inside the VPS guest OS — useful when an application or user workflow conflicts with quota limits. Queues a `disable_quota` action on the hypervisor (`vpsqueue`) and triggers a VNC re-setup. Side-effecting GET. Reversible via `doVpsEnableQuota`. Sibling op: `doVpsEnableQuota`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes.\n\n**Side effects:**\n- Inserts `vpsqueue` `disable_quota` row.\n- Calls `vps_resetup_vnc()`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Re-enable:** `doVpsEnableQuota`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/eject_cd": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsEjectCd",
        "summary": "Eject the mounted ISO from the VPS virtual CD drive (keep the drive)",
        "description": "Unmounts whatever ISO is currently in the VPS virtual CD drive, leaving the drive attached so another ISO can be mounted. Distinct from `doVpsDisableCd` (which removes the drive itself). Queues an `eject_cd` action on the hypervisor (`vpsqueue`) and triggers a VNC re-setup. Side-effecting GET. Sibling ops: `getVpsInsertCd` (list available ISOs), `postVpsInsertCd` (mount a different one), `doVpsDisableCd` (remove drive entirely).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes.\n\n**Side effects:**\n- Inserts `vpsqueue` `eject_cd` row.\n- Calls `vps_resetup_vnc()`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Mount a different ISO:** `postVpsInsertCd`.\n- **Remove the drive entirely:** `doVpsDisableCd`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/enable_quota": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsEnableQuota",
        "summary": "Enable per-user disk quota enforcement inside the VPS guest OS",
        "description": "Turns on per-user disk-quota enforcement inside the VPS guest OS. Queues an `enable_quota` action on the hypervisor (`vpsqueue`) and triggers a VNC re-setup. Side-effecting GET. Reversible via `doVpsDisableQuota`. Sibling op: `doVpsDisableQuota`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes.\n\n**Side effects:**\n- Inserts `vpsqueue` `enable_quota` row.\n- Calls `vps_resetup_vnc()`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Disable later:** `doVpsDisableQuota`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/insert_cd": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Available CD/ISO images for the VPS."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsInsertCd",
        "summary": "List ISO templates that can be mounted in the VPS virtual CD drive",
        "description": "Step 1 of the CD-mount flow. Returns the catalog of ISO images the customer can mount in the VPS virtual CD drive — typically rescue ISOs, OS installers, or recovery media. Read-only. Use to populate a CD/ISO picker before calling `postVpsInsertCd` with a chosen URL. Sibling ops: `postVpsInsertCd` (mount), `doVpsEjectCd` (unmount), `doVpsDisableCd` (remove drive), `doVpsRestart` (boot from mounted CD).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** Object with available ISO templates (current implementation returns the platform's CD options).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Next:** `postVpsInsertCd` (provide `url` for the ISO to mount).\n- **Boot from CD:** `doVpsRestart` after mounting.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/UrlRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UrlRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsInsertCd",
        "summary": "Mount an ISO image in the VPS virtual CD drive from a URL",
        "description": "Mounts an ISO image in the VPS virtual CD drive from the supplied URL — used to boot into rescue media, run an OS installer, or temporarily attach removable media. Queues an `insert_cd` action on the hypervisor (`vpsqueue`) with the URL. After mounting, restart the VPS with `doVpsRestart` to boot from the CD. Sibling ops: `getVpsInsertCd` (list), `doVpsEjectCd` (unmount), `doVpsDisableCd` (remove drive), `doVpsRestart` (boot from CD).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `url` (string, required) — http(s):// URL to a `.iso` file accessible from the hypervisor.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes.\n\n**Side effects:**\n- Inserts `vpsqueue` `insert_cd` row with the URL.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **List options first:** `getVpsInsertCd`.\n- **Boot the ISO:** `doVpsRestart`.\n- **Unmount when done:** `doVpsEjectCd`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/invoices": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get VPS Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsInvoices",
        "summary": "List all billing invoices associated with this specific VPS",
        "description": "Returns the billing history for one VPS — initial purchase invoice, monthly/period renewal invoices, addon invoices (extra IPs, additional disk space), and any prorated upgrade invoices for slice changes. Read-only. Backed by `Billing\\InvoicesList::go()`. Use to render a per-VPS billing-history view, to find an unpaid invoice id to pass to `initiatePayment`, or to confirm a recent charge. Sibling ops: `getVpsInfo`, `getBillingInvoice` (single invoice detail), `initiatePayment`, `addVps` (creates the first invoice).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `ChargeInvoiceRows` — array of invoice rows with `id`, `amount`, `paid`, `description`, `date`, `due_date`, `currency`, `module=vps`, `service={id}`, and any addon-specific fields. Order is most-recent-first.\n\n**Auth:** Session/API key. Ownership enforced via parent VPS.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Service Passed` — `id` not owned by caller.\n\n**Related calls:**\n- **Single invoice detail:** `getBillingInvoice`.\n- **Pay an unpaid invoice:** `initiatePayment` (`GET /billing/pay/{method}/{invoices}`).\n- **All invoices across account:** `getBillingInvoices`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/reinstall_os": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpsTemplatesList"
                }
              }
            },
            "description": "VPS Reinstall OS info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsReinstallOs",
        "summary": "List OS templates compatible with this VPS's hypervisor for reinstall",
        "description": "Step 1 of the OS-reinstall flow. Returns the list of OS templates that can be installed on this specific VPS — filtered server-side by the VPS's backing hypervisor type (KVM, HyperV, OpenVZ, Virtuozzo) and by `template_available=1` (non-admin callers only see published templates). Use to populate the reinstall picker; the `template_file` from a chosen row is what `postVpsReinstallOs` accepts. **Reinstall destroys all data** — recommend a backup via `getVpsBackup` first. Sibling ops: `postVpsReinstallOs` (commit reinstall), `getVpsBackup` (snapshot first), `postVpsRestore` (restore from backup instead).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `VpsTemplatesList`:\n- `templates` (array) — one entry per available template:\n  - `template_id` (integer)\n  - `template_name` (string) — display name (e.g. `CentOS 7`).\n  - `template_version` (string) — version (e.g. `7`).\n  - `template_file` (string) — **canonical identifier** to pass to `postVpsReinstallOs` (e.g. `centos-7-x86_64.qcow2`).\n  - `template_os` (string) — OS family tag.\n  - `template_type` (integer) — internal hypervisor type id.\n  - `template_available` (integer) — `1` for non-admin visible templates.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Snapshot before reinstall:** `getVpsBackup`.\n- **Commit reinstall:** `postVpsReinstallOs` (pass `template_file` + MyAdmin login password).\n- **Alternative — restore old backup:** `postVpsRestore`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/TemplateRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TemplateRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsReinstallOs",
        "summary": "Reinstall the VPS OS (DESTRUCTIVE — wipes disk; requires re-auth)",
        "description": "**DESTRUCTIVE.** Wipes the VPS disk and reinstalls the chosen OS template. Re-authenticates via the customer's MyAdmin account password (`auth->authenticate` against `account_lid`+`localPassword`), updates the `vps` row (`vps_server_status='Reinstalling'`, `vps_os=<template>`), saves any new root password to `history_log`, and queues a `reinstall_os` action on the hypervisor (`vpsqueue`). **No rollback** — recover by restoring a backup via `postVpsRestore` (must have been created beforehand). Allow ~2 minutes for reinstall to start. Sibling ops: `getVpsReinstallOs` (list templates), `getVpsBackup` (snapshot before reinstalling), `postVpsRestore` (alternative — restore from backup instead).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields (required):**\n- `template` (string, required) — `template_file` from `getVpsReinstallOs.templates[].template_file`. Rejected if not found in `vps_templates` for the VPS's `template_type`.\n- `localPassword` (string, required) — the customer's current MyAdmin account password (re-auth check).\n\n**Body fields (optional):**\n- `password` (string, optional) — new root password to set during reinstall. If absent, the template default is used.\n\n**Returns:** `{ text: \"Reinstall to has been sent to the server. Please allow up to 2 minutes for action to be completed.\" }`. If the VPS is `pending` rather than `active`, the OS selection is saved for activation and `{ text: \"OS selection has been updated in our system for when the service is activated.\" }` is returned.\n\n**Side effects:**\n- Updates `vps_server_status` and `vps_os` columns.\n- Inserts new password into `history_log` when provided.\n- Inserts `vpsqueue` `reinstall_os` row (active services only).\n\n**Auth:** Session/API key plus re-auth via `localPassword`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `Missing Account Password` — `localPassword` body field absent/empty.\n- `Invalid Account Password` — `localPassword` re-auth failed.\n- `This Template <name> does not exist` — `template` not found for this VPS's platform.\n\n**Related calls:**\n- **Prerequisite:** `getVpsReinstallOs` (find valid `template_file`).\n- **Recommended pre-step:** `getVpsBackup` (snapshot).\n- **Alternative (preserve state):** `postVpsRestore`.\n- **Verify after reinstall:** `getVpsInfo` (look for updated `vps_os`).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/reset_password": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Password reset requirements and current state."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsResetPassword",
        "summary": "Pre-flight check before resetting the VPS root password to a random value",
        "description": "Step 1 of the random-root-password reset flow. Validates ownership and active status; the response describes the reset behavior (currently a passthrough — full implementation reserved for future policy fields). Read-only. Use to confirm the VPS exists and is active before triggering the reset via `postVpsResetPassword`. For a customer-chosen password use `postVpsChangeRootPassword` instead. Sibling ops: `postVpsResetPassword`, `postVpsChangeRootPassword`, `postVpsChangeWebuzoPassword`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** Object with reset options (reserved for policy info).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Next (random):** `postVpsResetPassword`.\n- **Specific password instead:** `postVpsChangeRootPassword`.\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsResetPassword",
        "summary": "Reset the VPS root password to a server-generated random value",
        "description": "Resets the VPS root/Administrator password to a fresh random value generated on the hypervisor — typical when the customer has lost the existing password and cannot recover it. Queues a `reset_password` action on the hypervisor (`vpsqueue`). The new password is delivered via the standard hypervisor password-change channel (logged to `history_log` and surfaced through `getVpsWelcomeEmail` / dashboard credential views). **No rollback** — to undo, set a new password via `postVpsChangeRootPassword` or call `postVpsResetPassword` again. Sibling ops: `getVpsResetPassword`, `postVpsChangeRootPassword`, `postVpsChangeWebuzoPassword`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None — password is generated server-side.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes.\n\n**Side effects:**\n- Inserts `vpsqueue` `reset_password` row; hypervisor agent generates and applies the new password.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Specific password instead:** `postVpsChangeRootPassword`.\n- **Retrieve the new credentials:** `getVpsWelcomeEmail` (resends with current credentials).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/restart": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsRestart",
        "summary": "Reboot the VPS — preferred over stop+start for software changes",
        "description": "Reboots the VPS — typically after a kernel update, configuration change, or to recover from an unresponsive state. **Preferred over `doVpsStop` followed by `doVpsStart`** because it preserves the boot context and lets the hypervisor handle the sequence atomically. Queues a `restart` action on the hypervisor (`vpsqueue`) and triggers a VNC re-setup. Side-effecting GET. Idempotent in effect — restarting a running VPS reboots it, restarting a stopped VPS starts it. Sibling ops: `doVpsStart`, `doVpsStop`, `getVpsInfo`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes; poll `getVpsInfo` to confirm state.\n\n**Side effects:**\n- Inserts `vpsqueue` `restart` row.\n- Calls `vps_resetup_vnc()` to refresh the VNC config.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active` — also returned for `cancelled`/`suspended` services.\n\n**Related calls:**\n- **Verify state:** `getVpsInfo`.\n- **Power off only:** `doVpsStop`.\n- **Power on:** `doVpsStart`.\n- **Boot from CD:** `postVpsInsertCd` then `doVpsRestart`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/reverse_dns": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReverseDnsEntries"
                }
              }
            },
            "description": "VPS Reverse DNS info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsReverseDns",
        "summary": "Read the current PTR (reverse-DNS) records for every IP on the VPS",
        "description": "Returns the PTR/reverse-DNS hostname currently resolving for every IP attached to the VPS — primary `vps_ip` plus any extras purchased via `postVpsBuyIp`. PTRs are read live via `get_hostname()` (DNS lookup), not cached. Read-only. Sibling ops: `postVpsReverseDns` (update entries), `getVpsBuyIp` (add more IPs first), `postVpsChangeHostname` (auto-updates PTR for the primary IP).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `ReverseDnsEntries` — `{ ips: { \"<ip>\": \"<ptr-hostname>\", ... } }`. Empty string for IPs with no PTR set.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Update PTRs:** `postVpsReverseDns`.\n- **Add IPs first:** `getVpsBuyIp` → `postVpsBuyIp`.\n- **Hostname change (auto-PTR for primary):** `postVpsChangeHostname`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextResponse"
                }
              }
            },
            "description": "Update VPS Reverse DNS response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsReverseDns",
        "summary": "Bulk-update PTR (reverse-DNS) records for one or more VPS IPs",
        "description": "Sets the PTR hostname for each VPS IP — bulk update via the `ips` map. Calls `reverse_dns($ip, $newHostname)` for every IP in the body whose value differs from the current PTR; IPs not currently attached to the VPS are silently ignored. Propagation depends on the reverse-zone TTL but is typically minutes, not instant. Sibling ops: `getVpsReverseDns`, `postVpsChangeHostname` (auto-sets PTR for primary IP), `getVpsBuyIp`/`postVpsBuyIp` (add more IPs first).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `ips` (object, required) — `{ \"<ip>\": \"<new-hostname>\", ... }`. Only IPs that already belong to the VPS will be updated; others are ignored. Empty-string values are skipped.\n\n**Returns:** `{ message: \"DNS Updated\", success: true }`.\n\n**Side effects:**\n- One `reverse_dns()` call per IP whose value changed.\n- PTR records are written to the in-addr.arpa zone; propagation depends on TTL.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Read current PTRs first:** `getVpsReverseDns`.\n- **For the primary IP:** `postVpsChangeHostname` (renames the VPS hostname and auto-PTRs in one go).\n- **Adding IPs:** `getVpsBuyIp` → `postVpsBuyIp` → `postVpsReverseDns`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/setup_vnc": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Get VPS Setup VNC Information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsSetupVnc",
        "summary": "Read current VNC console connection info for the VPS",
        "description": "Returns the VNC IP/port the customer can connect to for an out-of-band console (KVM-style remote console — useful when SSH/RDP is unavailable, during boot, or for rescue work). Read-only. **Note:** the current implementation is a stub for some platforms — if you get an empty response, call `postVpsSetupVnc` to (re)provision the VNC endpoint, then call this again. Sibling ops: `postVpsSetupVnc`, `getVpsViewDesktop` (Windows GUI access via RDP/HTML5).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** Object with VNC connection info (IP, port, credentials when provisioned).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Provision/refresh VNC:** `postVpsSetupVnc`.\n- **Windows remote desktop:** `getVpsViewDesktop`.\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsSetupVnc",
        "summary": "Provision or refresh the VNC console endpoint for the VPS",
        "description": "(Re)establishes out-of-band VNC console access on the VPS — typically after a network change, hypervisor migration, or when troubleshooting boot issues. Validates the supplied VNC IP via `validIp()`, persists it to the `vps` row (`vps_vnc` column), and queues a `setup_vnc` action on the hypervisor (`vpsqueue`). Sibling ops: `getVpsSetupVnc` (read current), `getVpsViewDesktop` (Windows GUI/RDP path).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `vnc` (string, required) — IPv4 address for the VNC endpoint. Validated by `validIp()`.\n\n**Returns:** `{ text, queueId }` — allow ~2 minutes for the hypervisor to bring up the listener.\n\n**Side effects:**\n- Updates `vps.vps_vnc` to the new IP.\n- Inserts `vpsqueue` `setup_vnc` row.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `Invalid IP \"<value>\" or is blank` — `vnc` failed `validIp()`.\n\n**Related calls:**\n- **Read after provisioning:** `getVpsSetupVnc`.\n- **Windows GUI access:** `getVpsViewDesktop`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/slices": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "VPS Slices information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsSlices",
        "summary": "Read current slice count, min/max range, and prorated per-slice upgrade cost",
        "description": "Step 1 of the slice upgrade/downgrade flow. A \"slice\" bundles RAM, disk, and CPU on the VPS — the smallest unit of vertical scaling. Returns the current slice count (`vps_slices`), the range available (`min_slices = current`, `max_slices = host capacity`), the per-slice recurring cost (after coupon and frequency discount), the prorated cost for the remainder of the current cycle, and the underlying resource units (`slice_ram` in MB, `slice_hd` in GB). Read-only. Use to render an upgrade picker before calling `postVpsSlices`. Sibling ops: `postVpsSlices` (commit), `postVpsBuyHdSpace` (disk-only addon).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:**\n- `min_slices` (integer) — current allocation; floor for downgrades is also this value (downgrade decreases from here).\n- `max_slices` (integer) — host-capacity-limited upper bound (`get_vps_max_slices()`).\n- `slice_cost` (float) — per-slice recurring cost in customer currency.\n- `prorated_slice_cost` (float) — prorated cost for the remainder of the current billing cycle.\n- `frequency` (integer) — billing cycle in months (1/6/12/24/36).\n- `slice_ram` (integer) — RAM (GB) per slice.\n- `slice_hd` (integer) — disk (GB) per slice. For KVM Storage (service-type 57) this comes from `services_field2.slice_hd`.\n- `vps_slices` (integer) — current count.\n- `vps_cost` (float) — current monthly cost.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Next:** `postVpsSlices` (commit; creates prorated invoice for upgrades, opens ticket for active services).\n- **Disk-only:** `getVpsBuyHdSpace`/`putVpsBuyHdSpace`/`postVpsBuyHdSpace`.\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Update VPS slices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsSlices",
        "summary": "Upgrade or downgrade the VPS slice count (creates prorated invoice on upgrade)",
        "description": "Changes the VPS's slice count — bumps or shrinks CPU/RAM/disk allocation. Deletes any unpaid prior slice-upgrade invoices for this VPS, updates the recurring `repeat_invoices` row to the new slice count's cost, creates a one-off `invoices` row prorated for the rest of the cycle, and either auto-opens a support ticket (active services — slice upgrades typically need a manual hypervisor action) or queues `set_slices` directly. Downgrades complete free and immediately (`paid=1` zero-amount invoice). **Real money** on upgrades. Sibling ops: `getVpsSlices`, `postVpsBuyHdSpace`, `initiatePayment`, `getVpsInfo`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `slices` (integer, required) — target slice count. `min_slices ≤ slices ≤ max_slices` per `getVpsSlices`. Must differ from current.\n\n**Returns:**\n- **Upgrade with payment owed:** `{ text: \"Thank you for your upgrade request. Kindly Pay the invoice to activate the upgrade.\", invoice: <integer> }` — pay via `initiatePayment`.\n- **Active VPS, upgrade queued via ticket:** `{ text: \"Thank you for your upgrade request. A ticket has been automatically opened for you. Please allow us 24 hours to complete your upgrade. ...\" }`.\n- **Downgrade:** `{ text: \"You have downgraded N slices from your VPS, the changes will be apply shortly.\" }`.\n\n**Side effects:**\n- Deletes unpaid `N Slice Upgrade for VPS {id}` invoices.\n- Updates `repeat_invoices` recurring cost and description (`<plan_name> N Slices`).\n- Creates a new `invoices` row for the prorated upgrade amount (or `paid=1` zero-amount for downgrades).\n- Updates `vps.vps_slices` and `vps.vps_currency`.\n- **Active VPS:** opens a support ticket via `create_ticket()` so an operator can resize manually, OR (when `$deferUpgradeViaTicket` is false) inserts `vpsqueue` `set_slices` row.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `Must pass the slices field.` — body field missing.\n- `Invalid Slices. You requested N but that amount is not available currently.` — out of range.\n- `No Changes were made, Please Try Again!` — equals current.\n- `Please activate the service first.` — VPS not in `pending`/`active`.\n\n**Related calls:**\n- **Prerequisite:** `getVpsSlices` (read range + prorated cost).\n- **Pay the upgrade invoice:** `initiatePayment` with the returned `invoice` id.\n- **Disk-only addon:** `postVpsBuyHdSpace`.\n- **Verify:** `getVpsInfo`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/start": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsStart",
        "summary": "Power on a stopped VPS instance",
        "description": "Powers on a stopped VPS. Queues a `start` action on the hypervisor (`vpsqueue`) and triggers a VNC re-setup. Side-effecting GET, idempotent in practice — calling on an already-running VPS is a no-op at the hypervisor. The `vps_status` field at the service level remains `active` (status reflects billing/lifecycle, not running power state); use `getVpsTrafficUsage` or external monitoring to confirm the VPS is actually up. Sibling ops: `doVpsStop`, `doVpsRestart`, `getVpsInfo`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }` — `queueId` references the `queue_log` entry. Allow ~10–30 s for the hypervisor to act.\n\n**Side effects:**\n- Inserts `vpsqueue` `start` row.\n- Calls `vps_resetup_vnc()`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active` — service is `cancelled`, `suspended` (non-payment), or `pending`. Resolve via payment (`initiatePayment`) for `suspended`, or contact support for `cancelled`/`pending` issues.\n\n**Related calls:**\n- **Reboot instead:** `doVpsRestart` (preferred over stop+start).\n- **Power off:** `doVpsStop`.\n- **Current state:** `getVpsInfo`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/stop": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "VPS ID number.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "doVpsStop",
        "summary": "Power off a running VPS — billing continues until cancellation",
        "description": "Halts the VPS without rebooting — typical before manually triggering a snapshot, freeing hypervisor resources, or temporarily taking a workload offline. Queues a `stop` action on the hypervisor (`vpsqueue`) and triggers a VNC re-setup. Side-effecting GET, idempotent — stopping an already-stopped VPS is a no-op. **Billing continues while the VPS is stopped** — to stop both the workload and billing, use `VPSCancel`. Sibling ops: `doVpsStart`, `doVpsRestart`, `VPSCancel`, `getVpsBackup` (snapshot first).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `{ text, queueId }`. Allow ~10–30 s.\n\n**Side effects:**\n- Inserts `vpsqueue` `stop` row.\n- Calls `vps_resetup_vnc()`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Reboot instead:** `doVpsRestart` (preferred for software changes).\n- **Power on later:** `doVpsStart`.\n- **Snapshot first:** `getVpsBackup`.\n- **Stop billing too:** `VPSCancel`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/traffic_usage": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VpsTrafficResponse"
                }
              }
            },
            "description": "Get VPS Traffic usage"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsTrafficUsage",
        "summary": "Read bandwidth traffic usage data for the VPS",
        "description": "Returns bandwidth-consumption data for the VPS — inbound/outbound bytes per day and aggregated totals against the plan's `bwSlice` × `vps_slices` allowance. Pulled via `vps_bandwidth_data($vps_id)` from the bandwidth-tracking subsystem. Read-only. For custom date-range or granularity filters use `postVpsTrafficUsage` (currently mirrors GET behavior but reserved for filter parameters). Sibling ops: `postVpsTrafficUsage`, `getVpsInfo` (BW allowance fields are shown there).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `VpsTrafficResponse` — array/object with bandwidth usage points (timestamp, inbound bytes, outbound bytes, totals).\n\n**Auth:** Session/API key. Ownership enforced via parent VPS.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n\n**Related calls:**\n- **Filtered query:** `postVpsTrafficUsage` (reserved for date-range filters).\n- **Plan allowance:** `getVpsInfo` returns `bw_total` / `slices` you can compute against.\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Filtered traffic usage search results."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsTrafficUsage",
        "summary": "Search/filter VPS bandwidth usage with custom criteria (reserved)",
        "description": "Filtered variant of the bandwidth-usage endpoint — reserved for date-range and granularity filters. The current implementation mirrors `getVpsTrafficUsage` behavior and returns the full dataset; the body shape is reserved for future filter parameters (start/end date, day/hour granularity). Sibling op: `getVpsTrafficUsage`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** Filter fields (reserved — date range, granularity).\n\n**Returns:** Same `VpsTrafficResponse` shape as `getVpsTrafficUsage`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n\n**Related calls:**\n- **Unfiltered alternative:** `getVpsTrafficUsage`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/view_desktop": {
      "get": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Get VPS View Desktop Information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsViewDesktop",
        "summary": "Read remote-desktop (RDP/HTML5) connection info for a Windows/GUI VPS",
        "description": "Returns remote-desktop connection details for a Windows VPS or any VPS with a GUI session — IP, port, recommended client, and the `client_links` the dashboard surfaces for launching the session. Backed by `ViewVPS::getDetails()`; response shape mirrors `getVpsInfo` minus the `admin_links` block. Sibling ops: `postVpsViewDesktop` (refresh), `getVpsSetupVnc`/`postVpsSetupVnc` (low-level VNC console).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** Same shape as `getVpsInfo` (sans `admin_links`) — includes `client_links` with RDP/HTML5 launch URLs.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Refresh session:** `postVpsViewDesktop`.\n- **Low-level console:** `getVpsSetupVnc` / `postVpsSetupVnc`.\n"
      },
      "post": {
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "description": "Submit VPS View Desktop Information"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsViewDesktop",
        "summary": "Refresh the remote-desktop session connection info after IP/hostname changes",
        "description": "Force-refreshes the remote-desktop connection metadata — typically called after a hostname change (`postVpsChangeHostname`), IP addition (`postVpsBuyIp`), or reverse-DNS update (`postVpsReverseDns`) so the dashboard can re-fetch via its form-action pattern. Returns the same payload as `getVpsViewDesktop`. Sibling op: `getVpsViewDesktop`.\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None (form-action pattern).\n\n**Returns:** Same shape as `getVpsViewDesktop`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n\n**Related calls:**\n- **Read without refresh:** `getVpsViewDesktop`.\n- **Common triggers:** `postVpsChangeHostname`, `postVpsBuyIp`, `postVpsReverseDns`.\n\nPath param: `id` (integer). No body.\n\nReturns the VPS view-desktop payload. Errors: HTTP 404 wrong owner, HTTP 409 not `active`. Sibling:\n`getVpsViewDesktop`, `postVpsSetupVnc`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/websites": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebsiteRow"
                  }
                }
              },
              "multipart/form-data": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebsiteRow"
                  }
                }
              }
            },
            "links": {
              "getWebsiteById": {
                "operationId": "getWebsiteInfo",
                "parameters": {
                  "id": "$response.body#/0/website_id"
                },
                "description": "The `website_id` returned in each row can be used as the `id` parameter in `GET /websites/{id}` to retrieve full service details."
              }
            },
            "description": "The listing of `Websites` services on your account."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWebsiteList",
        "summary": "List the caller's webhosting (cPanel/DirectAdmin/Plesk/Webuzo) services",
        "description": "Enumerates every shared/reseller hosting account (\"website\") owned by the authenticated customer. The canonical entry point for discovering a `website_id` to pass into other webhosting endpoints. Filtered server-side by `website_custid = session account_id` — cross-customer leaks are not possible. Empty array means the account has no websites (not an error). Sibling ops: `getWebsiteInfo`, `getWebsitesLogin`, `getWebsitesBackups`, `getWebsiteInvoices`, `webhostingCancel`, `getNewWebsite` (order a new one).\n\n**Path/Query/Body:** None.\n\n**Returns:** Array of `WebsiteRow` — per-website summary:\n- `website_id` (integer) — canonical id used in `/websites/{id}/*` paths.\n- `website_hostname` (string) — primary FQDN.\n- `website_status` (string enum) — `pending` / `active` / `pending-cancel` / `canceled`.\n- `services_name` (string) — plan/package label (e.g. `Standard`, `Reseller`).\n- `repeat_invoices_cost` (decimal) — current recurring cost in the website's billing currency.\n- `website_comment` (string|null) — customer-provided note.\n\n**Auth:** Session/API key. Ownership filter enforced via `website_custid`.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Per-website detail:** `getWebsiteInfo` (full).\n- **Control panel:** `getWebsitesLogin` (auto-login URL).\n- **Backups + restore points:** `getWebsitesBackups`.\n- **Billing:** `getWebsiteInvoices`.\n- **Order a new site:** `getNewWebsite` → `putWebsites` → `addWebsite`.\n- **Cancel:** `webhostingCancel`.\n"
      }
    },
    "/websites/order": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebsitesOrder"
                }
              }
            },
            "links": {
              "validateWebsiteOrder": {
                "operationId": "putWebsites",
                "description": "After reviewing available plans, use `PUT /websites/order` to validate your order before placing it."
              },
              "placeWebsiteOrder": {
                "operationId": "addWebsite",
                "description": "Use `POST /websites/order` to place an order for a new webhosting package."
              }
            },
            "description": "Website ordering details including available plans and pricing."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getNewWebsite",
        "summary": "Read the webhosting order catalog — plans, packages, promo offers, pricing",
        "description": "Step 1 of the webhosting order flow. Returns the full ordering catalog needed to build a valid order: available `serviceTypes` (plans), `serviceOffers` (promotional bundles), `packages`, billing `period` options, the customer's currency symbol, default `serviceOfferId`, and `enableDomainRegistering` (whether free/paid domain registration is bundled). Read-only — no service or invoice created. Sibling discovery endpoints in other modules: `getNewVps`, `getNewMail`, `getNewDomain`. Sibling order-flow ops: `putWebsites`, `addWebsite`.\n\n**Path/Query/Body:** None.\n\n**Returns** (schema `WebsitesOrder`):\n- `currencySymbol` (string) — locale currency symbol for display.\n- `step` (integer) — current step in the multi-step order wizard.\n- `website` (integer) — pre-selected default plan id.\n- `period` (integer) — pre-selected default billing frequency.\n- `serviceOfferId` (integer) — pre-selected promo offer.\n- `serviceTypes` (array) — every plan; `services_ourcost` stripped server-side. Keys per row: `services_id`, `services_name`, `services_cost`, `services_type` (`WEB_CPANEL` / `WEB_DIRECTADMIN` / `WEB_PLESK` / `WEB_VESTA` / `WEB_PPA` / `WEB_WORDPRESS` / `WEB_STORAGE`), etc.\n- `serviceOffers` (array) — current promotional bundles.\n- `packages`, `packges` (array — legacy field name preserved alongside `packages`).\n- `enableDomainRegistering` (bool) — when `true`, the order can also register/transfer a domain.\n- `jsonServices`, `jsonServiceOffers` (string) — JSON-encoded copies for inline use in HTML.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Next:** `putWebsites` (validate + quote — no charge), `addWebsite` (place order).\n\n**Example abridged response:**\n```json\n{\n  \"currencySymbol\": \"$\",\n  \"step\": 1,\n  \"website\": 23,\n  \"period\": 1,\n  \"enableDomainRegistering\": true,\n  \"serviceTypes\": [\n    {\"services_id\": 23, \"services_name\": \"Standard\", \"services_cost\": 8.00, \"services_type\": 1},\n    {\"services_id\": 25, \"services_name\": \"Reseller\", \"services_cost\": 24.95, \"services_type\": 1}\n  ]\n}\n```\n"
      },
      "put": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "description": "Validate Website order response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "putWebsites",
        "summary": "Validate a webhosting order and preview cost — dry run, no charge",
        "description": "Step 2 of the webhosting order flow. Dry-runs the order through `validate_buy_website()`: checks `hostname` against `valid_hostname()` and the keyword blocklist, validates `packageId` against the customer's plan eligibility, confirms the chosen plan's hypervisor pool is in stock (`OUTOFSTOCK_WEBHOSTING_*` constants), applies any coupon and frequency discount, and returns a cost preview plus any validation errors. No invoice or service record is created. **Always call before `addWebsite`** to surface coupon/pricing/hostname problems cheaply. Sibling ops: `getNewWebsite` (catalog), `addWebsite` (place order).\n\n**Body fields (form or JSON):**\n- `hostname` (string, required) — primary FQDN for the website. Must pass `valid_hostname()`, must not contain `interserver.net` (non-admin), must not be on the blocked-keyword list, must match the plan's TOS rules.\n- `rootpass` (string, optional) — control-panel admin password; if blank, a random 8-char password is generated server-side via `generateRandomString(8,1,1,1,1)`.\n- `packageId` (integer, required) — plan id from `getNewWebsite.serviceTypes[].services_id`. Must have `services_module='webhosting'` and `services_buyable=1` (non-admin).\n- `period` (integer, optional, default 1) — billing cycle in months: 1 / 6 / 12 / 24 / 36. Same frequency discounts as VPS apply.\n- `coupon` (string, optional) — coupon code.\n- `serviceOfferId` (integer, optional) — promo bundle from `getNewWebsite.serviceOffers`.\n- `script` (integer, optional, default 0) — auto-installer id (Softaculous/WordPress/etc., 0 = none).\n- `comment` (string, optional) — free-form note saved on the service row.\n- `registerDomain` (bool, optional) — when `true` and `enableDomainRegistering=true` from the catalog, also registers/transfers the domain through the order.\n- Implicit: TOS acceptance (validated via `tos='yes'` in source — required for non-admin).\n\n**Returns** (validation envelope):\n- `continue` (bool) — `true` if the order can safely be POSTed.\n- `errors` (array of strings) — human-readable validation messages.\n- `frequency` (integer) — resolved billing frequency.\n- `coupon` (string) — the applied coupon name (echoed).\n- `couponCode` (integer) — the matched coupon row id, or `0` if none.\n- `serviceType` (integer) — resolved plan id.\n- `serviceCost` (float) — first-period total cost (includes coupon + period discount).\n- `originalCost` (float) — undiscounted reference.\n- `repeatServiceCost` (float) — recurring cost after discounts.\n- `hostname`, `password` (string) — final sanitized values (may differ from input — e.g. random password generated).\n- `introFrequency` (integer) — first-period bonus length (intro pricing).\n\n**Side effects:** None — pure read.\n\n**Auth:** Session/API key.\n\n**Errors (within `errors` array, `continue=false`):**\n- `Invalid Billing Interval` — `period` not numeric.\n- `All webhosting servers are currently full.` — `OUTOFSTOCK_WEBHOSTING`.\n- `Invalid Package Specified.` — plan id not in the webhosting module or not buyable.\n- `Our <Plan> Webhosting Servers are currently full.` — plan-specific stock check.\n- `The hostname cannot contain interserver.net`.\n- `Hostname \"<x>\" Contains Invalid Characters Or Is Blank`.\n- `Hostname contains a blocked keyword.`.\n- `You must agree to the terms of service and click the checkbox saying so.`.\n- `Invalid Coupon Specified` — coupon not usable for this plan/customer.\n\nTop-level HTTP errors: `401` unauthenticated.\n\n**Related calls:**\n- **Prerequisite:** `getNewWebsite` (catalog).\n- **Next:** `addWebsite` (same body — actually places the order).\n\n**Example request body:**\n```json\n{\n  \"hostname\": \"mystore.example.com\",\n  \"rootpass\": \"Sup3rS3cret!\",\n  \"packageId\": 23,\n  \"period\": 12,\n  \"coupon\": \"\",\n  \"registerDomain\": false,\n  \"script\": 0\n}\n```\n"
      },
      "post": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/ServiceOrderPostResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "x-payment-info": {
          "intent": "session",
          "method": "card",
          "amount": 0,
          "currency": "USD",
          "description": "Order a shared or reseller web hosting account. Returns invoice IDs for checkout.",
          "method_options": [
            "cc",
            "paypal",
            "btcpay",
            "coinbase",
            "payu",
            "ccavenue",
            "cashfree",
            "payssion",
            "prepay"
          ]
        },
        "operationId": "addWebsite",
        "summary": "Place a new webhosting order, create the invoice, and queue provisioning",
        "description": "Step 3 of the webhosting order flow — actually places the order. Revalidates via `validate_buy_website()` (same checks as `putWebsites`), then calls `place_buy_website()` to allocate a backing webhosting server, create the `webhosting` service row in `pending` status, generate a `Repeat_Invoice` recurring billing row, produce an initial `invoices` row, and (when `registerDomain=true`) also kick off a domain order with its own invoice. The activator runs once the invoice is paid; `getWebsitesWelcomeEmail` then fires automatically with control-panel credentials. **Real money** — call `putWebsites` first to preview cost. Sibling ops: `getNewWebsite`, `putWebsites`, `getWebsiteInfo`, `webhostingCancel`.\n\n**Body fields:** Identical to `putWebsites`. Required: `hostname`, `packageId`. Optional: `rootpass` (auto-generated if blank), `period`, `coupon`, `serviceOfferId`, `script`, `comment`, `registerDomain`.\n\n**Returns** (schema `ServiceOrderPostResponse`):\n- `total_cost` (string/decimal) — total to pay across all generated invoices.\n- `iid` (string) — primary invoice id (numeric).\n- `iids` (array) — tagged invoice ids (e.g. `SERVICEwebhosting12345`).\n- `real_iids` (array) — numeric invoice ids to pass to `initiatePayment`.\n- `serviceId` (integer) — new `website_id`; use with `getWebsiteInfo` to poll status.\n- `invoice_description` (string) — human-readable summary.\n- `cj_params` (object) — Commission Junction tracking parameters.\n\n**Side effects:**\n- Inserts `webhosting` service row (`website_status='pending'`).\n- Inserts `repeat_invoices` row for recurring charge.\n- Inserts `invoices` row for the first period.\n- When `registerDomain=true`: also creates a domain service row and its own invoice (`domain_serviceid`, `diid` returned alongside).\n- Hashes/encrypts `rootpass` to `history_log`.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- When validation fails: response is the same `errors` array from `putWebsites` (HTTP 200 with `continue=false` shape).\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Prerequisite:** `getNewWebsite`, `putWebsites`.\n- **Next:** `getBillingInvoice` (confirm), `initiatePayment` (pay with `real_iids`), then poll `getWebsiteInfo` until `website_status=='active'`.\n- **Resend credentials after activation:** `getWebsitesWelcomeEmail`.\n- **Cancel before paying:** `webhostingCancel` (or `deleteBillingInvoice` for the pending invoice).\n\n**Full ordering happy path:**\n```text\nGET /websites/order                              -> catalog (getNewWebsite)\nPUT /websites/order { ...config }                -> price quote (putWebsites)\nPOST /websites/order { ...config }               -> { serviceId, real_iids } (addWebsite)\nGET /billing/invoices/{iid}                      -> confirm invoice (getBillingInvoice)\nGET /billing/pay/cc/{real_iids[0]}               -> pay (initiatePayment)\nGET /websites/{serviceId}                        -> poll until website_status==\"active\"\nGET /websites/{serviceId}/welcome_email          -> resend credentials if needed\n```\n"
      }
    },
    "/websites/{id}": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The website service ID. Use `website_id` from `GET /websites`.",
            "schema": {
              "type": "integer"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Website"
                }
              }
            },
            "links": {
              "getWebsiteInvoices": {
                "operationId": "getWebsiteInvoices",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Retrieve invoices for this website service."
              },
              "getWebsiteLogin": {
                "operationId": "getWebsitesLogin",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Get an auto-login URL for the webhosting control panel."
              },
              "getWebsiteBackups": {
                "operationId": "getWebsitesBackups",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "List available backups for this website."
              },
              "cancelWebsite": {
                "operationId": "webhostingCancel",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Cancel this website service."
              }
            },
            "description": "Website details"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWebsiteInfo",
        "summary": "Read full configuration and status detail for one webhosting service",
        "description": "Returns everything the customer dashboard shows for one website — status, hostname, control-panel username, primary IP, host server, plan, billing summary, action `client_links`, and supported addons. Read-only. Backed by `ViewWebsite::getDetails()`. Internal `admin_links`, `settings`, `csrf`, and `serviceMaster.website_key` (the API key) are stripped before return. Use to render a website detail page, verify ownership before mutating, or poll `website_status` after `addWebsite`. Sibling ops: `getWebsiteList`, `getWebsitesLogin`, `getWebsitesBackups`, `getWebsiteInvoices`, `webhostingCancel`.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** None.\n\n**Returns** (schema `Website`):\n- `serviceInfo` — `website_id`, `website_hostname`, `website_username`, `website_ip`, `website_server`, `website_type` (plan id), `website_status`, `website_comment`.\n- `serviceMaster` — host-server row (cPanel/DA/Plesk hostname, panel URL). `website_key` is stripped.\n- `serviceType` — plan row (`services_ourcost` stripped).\n- `client_links` (array) — `{name, link, icon}` for restart, login, backup, etc. Internal `?link=queue&action=...` URLs are pre-resolved to plain action names.\n- `serviceAddons` — extra IPs, additional resources.\n\n**Auth:** Session/API key. Ownership enforced via `website_custid`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` (legacy text — webhosting reuses the helper) — `id` not owned by caller.\n\n**Related calls:**\n- **Login to panel:** `getWebsitesLogin` (auto-login URL).\n- **Backups + restore:** `getWebsitesBackups`.\n- **Billing:** `getWebsiteInvoices`.\n- **Reverse DNS:** `gettWebsiteReverseDns`, `postWebsitesReverseDns`.\n- **Buy extra IP:** `getWebsiteBuyIp`, `postWebsiteBuyIp`.\n- **Migration:** `postWebsiteMigration`.\n- **Resend welcome email:** `getWebsitesWelcomeEmail`.\n- **Cancel:** `webhostingCancel`.\n"
      },
      "post": {
        "tags": [
          "Webhosting"
        ],
        "parameters": [
          {
            "name": "id",
            "description": "The website service ID. Use `website_id` from `GET /websites`.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateWebsiteInfo",
        "summary": "POST mutation hook for the website detail page (use dedicated ops where possible)",
        "description": "POST mutation hook for the website detail page. The implementation currently routes through the same `View::go()` handler as `getWebsiteInfo`; concrete update behavior depends on which `client_links` action the form is driving. **For specific changes, prefer the dedicated endpoints** — they enforce field-level validation and queue the correct hypervisor/panel actions. Sibling ops: `getWebsiteInfo`, all dedicated mutation endpoints below.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** Form-encoded fields appropriate to the `client_links` action being driven.\n\n**Returns:** `SuccessTextResponse` — `{text: \"...\"}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n\n**Prefer these dedicated endpoints:**\n- **Buy a paid IP or update reverse DNS:** `postWebsiteBuyIp` (the latter via `action=reverse_dns`).\n- **PTR-only changes:** `postWebsitesReverseDns`.\n- **Migrate site from another host:** `postWebsiteMigration`.\n- **Resend control-panel credentials:** `getWebsitesWelcomeEmail`.\n- **Auto-login to cPanel/DA/Plesk:** `getWebsitesLogin`.\n- **Cancel:** `webhostingCancel`.\n"
      },
      "delete": {
        "tags": [
          "Webhosting"
        ],
        "parameters": [
          {
            "examples": {
              "WebsiteExample": {
                "value": "123"
              }
            },
            "name": "id",
            "description": "The website service ID. Use `website_id` from `GET /websites`.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/WebsiteCancelResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "webhostingCancel",
        "summary": "Schedule termination of a webhosting service — wipes panel account at cycle end",
        "description": "**DESTRUCTIVE.** Schedules the website for cancellation via the shared `Billing\\CancelService::go($id)` flow with `module='webhosting'`. Marks the service `pending-cancel`, halts the recurring invoice, and queues deprovisioning so cPanel/DirectAdmin/Plesk/Webuzo removes the account and **all hosted files, databases, mailboxes, and DNS** at end-of-cycle. **There is no client-side restore** — take a cpmove backup via `getWebsitesBackups` first (with `download=<name>`) if data must be preserved. Sibling ops: `getWebsitesBackups`, `getWebsiteInfo` (verify status flipped), `getWebsiteInvoices`.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** None.\n\n**Returns:** `WebsiteCancelResponse` — cancel-service confirmation payload.\n\n**Side effects:**\n- Sets `website_status='pending-cancel'`.\n- Marks the `repeat_invoices` row as non-renewing.\n- Logs the cancellation in `history_log`.\n- Queues deprovisioning to run at end-of-cycle (the cPanel/DA/Plesk account, all hosted files, databases, email accounts, and DNS will be removed).\n- Customer retains panel access until the cycle ends.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — service in a state that cannot be cancelled (already `canceled`, etc.).\n\n**Related calls:**\n- **Before cancelling:** `getWebsitesBackups` (download a cpmove archive — irretrievable after deprovisioning).\n- **After cancelling:** `getWebsiteInfo` (confirm `pending-cancel`), `getWebsiteInvoices` (final invoices).\n- **Sibling cancels on other modules:** `VPSCancel`, `CancelDomain`, `mailCancel`, etc. all use the same `CancelService` handler.\n"
      }
    },
    "/websites/{id}/buy_ip": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ips": {
                      "description": "A map of IP addresses to their current reverse DNS hostnames.",
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            },
            "description": "Current IP addresses and their reverse DNS hostnames for the website."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWebsiteBuyIp",
        "summary": "Read website IPs, current reverse DNS, and additional-IP pricing",
        "description": "Combined IP/billing view for a website: returns the primary `website_ip` plus any addon extras, each mapped to its current PTR hostname (via `get_hostname()`), the list of existing additional-IP repeat invoices (with `cancel_link` URLs), the count of paid extras, and the per-IP cost (in the website's billing currency, falling back to USD/`WEBSITE_IP_COST`). Read-only. Use to populate a \"buy another IP\" form or to audit current IP allocations. Sibling ops: `postWebsiteBuyIp` (buy or update PTR), `gettWebsiteReverseDns` (PTR-only view), `postWebsitesReverseDns` (PTR-only update).\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** None.\n\n**Returns:**\n- `ips` (object) — `{\"<ipv4>\": \"<ptr-hostname>\", ...}` for every IP attached.\n- `ipsDetails` (array) — existing addon invoices with each row's `ip`, `cancel_link` (`cancel_addon?module=webhosting&r=<rid>`), invoice metadata.\n- `ipCount` (integer) — count of paid addon IPs.\n- `ipCost` (float) — per-IP recurring cost in `currency`.\n- `currency` (string), `currencySymbol` (string).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Website Passed` — `id` not owned by caller.\n- `409 Website is not active` — `website_status != \"active\"`.\n\n**Related calls:**\n- **Buy another IP:** `postWebsiteBuyIp`.\n- **Update PTRs only:** `postWebsitesReverseDns` (or `postWebsiteBuyIp` with `action=reverse_dns`).\n- **Cancel an addon IP:** follow the `cancel_link` URL.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ips": {
                    "description": "A map of IP addresses to their desired reverse DNS hostnames.",
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "ips": {
                    "description": "A map of IP addresses to their desired reverse DNS hostnames.",
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    },
                    "success": {
                      "type": "boolean"
                    }
                  }
                }
              }
            },
            "description": "DNS update result."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postWebsiteBuyIp",
        "summary": "Buy an additional IP for the website OR update reverse DNS records",
        "description": "Dual-purpose mutation that branches on the `action` body field. **`action=buy_ip`** (default): allocates a new addon IP via `website_addon_get_free_ips`, creates an addon `repeat_invoices` row at `WEBSITE_IP_COST` (currency-converted to the parent invoice's currency), and emits a one-period `invoices` row to fund the first month — provisioning waits on payment and free-IP availability on the host server. **Real money**. **`action=reverse_dns`**: skips billing entirely and updates PTR records via `reverse_dns()` for any IP in the `ips` map whose new hostname differs from the current `get_hostname()` value. Sibling ops: `getWebsiteBuyIp` (preview), `gettWebsiteReverseDns` / `postWebsitesReverseDns` (PTR-only).\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body fields:**\n- `action` (string, optional, default `buy_ip`) — `buy_ip` or `reverse_dns`.\n- For `action=reverse_dns`: `ips` (object, required) — `{\"<ip>\": \"<new-hostname>\", ...}`. Only IPs already on the website are updated; others ignored. Empty-string values skipped.\n\n**Returns:**\n- For `buy_ip`: `{text: \"Ordered Additional IP successfully.\", invoice: <integer>, repeatInvoice: <integer>}`.\n- For `reverse_dns`: `{message: \"DNS Updated\", success: true}`.\n\n**Side effects:**\n- `buy_ip`: inserts `repeat_invoices` row (`Additional IP for Webhosting <id>`) and an `invoices` row for the first period.\n- `reverse_dns`: writes PTR records to the in-addr.arpa zone for changed IPs.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Website Passed`.\n- `409 Website is not active`.\n- `No available free IPs on this server. Please contact support to order additional IPs.` — host has no free IPs.\n\n**Related calls:**\n- **Preview first:** `getWebsiteBuyIp`.\n- **Pay the new addon invoice:** `initiatePayment` with the returned `invoice`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The website service ID. Use `website_id` from `GET /websites`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/websites/{id}/invoices": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChargeInvoiceRows"
                }
              }
            },
            "description": "Get Invoices response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWebsiteInvoices",
        "summary": "List all billing invoices and recurring charges scoped to one website",
        "description": "Returns the billing history for one webhosting service — initial purchase invoice, recurring monthly/period invoices, and any IP-addon invoices created via `postWebsiteBuyIp`. Backed by `Billing\\InvoicesList::go()` with `module='webhosting'` (same handler pattern as VPS/Mail/etc. per-service invoice endpoints). Use to render a per-website billing-history view or find an unpaid invoice id to pass to `initiatePayment`. Sibling ops: `getBillingInvoice`, `initiatePayment`, sibling cross-module: `getVpsInvoices`, `getDomainInvoices`, `getMailInvoices`. For account-wide history use top-level `getBillingInvoices`.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** None.\n\n**Returns:** `ChargeInvoiceRows` — array of invoice rows: `id`, `amount`, `paid`, `description`, `date`, `due_date`, `currency`, `module=webhosting`, `service={id}`.\n\n**Auth:** Session/API key. Ownership enforced via parent website.\n\n**Errors:**\n- `401` — unauthenticated.\n- `400 Invalid Service` — `id` not owned by caller.\n\n**Related calls:**\n- **Single invoice detail:** `getBillingInvoice`.\n- **Pay an unpaid invoice:** `initiatePayment`.\n- **Account-wide history:** `getBillingInvoices`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The website service ID. Use `website_id` from `GET /websites`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/websites/{id}/login": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebsiteLoginResponse"
                }
              }
            },
            "description": "Get Website Login response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWebsitesLogin",
        "summary": "Get a one-time auto-login URL for the website's control panel",
        "description": "Returns a single-use auto-login URL so the customer can jump into their control panel without entering credentials. Branches on `serviceMaster.website_type`:\n- **WEB_CPANEL** (default): calls WHM `create_user_session` for the `cpaneld` service, returns a session-bound cPanel URL.\n- **WEB_DIRECTADMIN**: calls DA `CMD_API_LOGIN_KEYS` (`max_uses=2`, IP-locked to `127.0.0.1` plus the caller's `client_ip`); returns one-time URL.\n- **WEB_PLESK**: calls Plesk SDK `createSession`, returns `https://<host>:8443/enterprise/rsession_init.php?PLESKSESSID=...`.\n- **WEB_PPA**, **WEB_VESTA**: placeholders (return `Unhandled Server Type`).\n\nSibling ops: `getWebsiteInfo`, `getWebsitesWelcomeEmail` (re-send credentials instead).\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** None.\n\n**Returns:** `{type: \"location\", location: \"<one-time-url>\"}`.\n\n**Side effects:**\n- WHM/DA/Plesk-side session creation; sessions usually expire after first use (DirectAdmin: `max_uses=2`, IP-locked).\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `Invalid Website Passed` — `id` not owned by caller.\n- `Website is <status>, only websites that are \"active\" can do this.` — `website_status != \"active\"`.\n- `No Host server or username` — service has no `website_username` or `website_server` resolved.\n- `Sorry! something went wrong, couldn't connect to <panel>!` — panel-side failure.\n- `Unhandled Server Type` — `website_type` is WEB_PPA / WEB_VESTA (or unrecognized).\n\n**Related calls:**\n- **If you need the credentials themselves:** `getWebsitesWelcomeEmail` (re-sends the welcome email with username/password).\n- **List sites first:** `getWebsiteList`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The website service ID. Use `website_id` from `GET /websites`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/websites/{id}/backups": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebsiteBackups"
                }
              }
            },
            "description": "Get Website Backups response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWebsitesBackups",
        "summary": "List off-site cpmove backups stored in Swift — list or inline-download archive",
        "description": "Returns the list of off-site cpmove backups stored for the webhosting account, or — with the `download=<name>` query param — inline-streams the chosen archive as base64. Backups are read from the OpenStack Swift container `serviceMaster.website_name` (authenticated with `SWIFT_WEBHOSTING_USER`/`SWIFT_WEBHOSTING_PASS`) and filtered to objects matching `cpmove-{website_username}-*`. Use to find restore points before a risky change or before `webhostingCancel`. Empty array means no off-site cpmoves have been pushed for this account. Sibling ops: `webhostingCancel` (snapshot before terminating), `getWebsiteInfo`.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Query params:**\n- `download` (string, optional) — when set to a backup `name` from the list, switches to inline download mode (returns the file base64-encoded). **Large payload** — only fetch when actually restoring.\n\n**Returns:**\n- **List mode** (no `download`): array of `{name: \"<cpmove-...>\", size: \"<human-scaled>\"}` (size from `Content-Length` via `Scale($len, 'bytes', 1)`).\n- **Download mode** (`?download=<name>`): single object `{name, size, file: \"<base64-encoded-archive>\"}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` (legacy text) — `id` not owned by caller.\n- `409 Website is not active` — `website_status != \"active\"`.\n\n**Related calls:**\n- **Take a backup before cancelling:** `getWebsitesBackups` (with `download=`) → `webhostingCancel`.\n- **Migrate to/from another host:** `postWebsiteMigration`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The website service ID. Use `website_id` from `GET /websites`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/websites/{id}/migration": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "custPortal": {
                    "description": "URL of the customer's current hosting portal.",
                    "type": "string"
                  },
                  "regEmail": {
                    "description": "Registered email address at the current host.",
                    "type": "string"
                  },
                  "password": {
                    "description": "Password for the current hosting account.",
                    "type": "string"
                  },
                  "ctrlPanel": {
                    "description": "URL of the current control panel.",
                    "type": "string"
                  },
                  "ftpUsername": {
                    "description": "FTP username at the current host.",
                    "type": "string"
                  },
                  "ftpPassword": {
                    "description": "FTP password at the current host.",
                    "type": "string"
                  },
                  "siteBusyMig": {
                    "description": "Information about site traffic during migration.",
                    "type": "string"
                  },
                  "splReqMig": {
                    "description": "Special requirements for the migration.",
                    "type": "string"
                  },
                  "domainReg": {
                    "description": "Whether domain registration assistance is needed.",
                    "type": "string"
                  },
                  "dataMig": {
                    "description": "Data migration timing preference.",
                    "type": "string"
                  },
                  "domainRegPortal": {
                    "description": "Domain registrar portal URL.",
                    "type": "string"
                  },
                  "domainRegEmail": {
                    "description": "Email for the domain registrar account.",
                    "type": "string"
                  },
                  "domainRegPassword": {
                    "description": "Password for the domain registrar account.",
                    "type": "string"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "custPortal": {
                    "type": "string"
                  },
                  "regEmail": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  },
                  "ctrlPanel": {
                    "type": "string"
                  },
                  "ftpUsername": {
                    "type": "string"
                  },
                  "ftpPassword": {
                    "type": "string"
                  },
                  "siteBusyMig": {
                    "type": "string"
                  },
                  "splReqMig": {
                    "type": "string"
                  },
                  "domainReg": {
                    "type": "string"
                  },
                  "dataMig": {
                    "type": "string"
                  },
                  "domainRegPortal": {
                    "type": "string"
                  },
                  "domainRegEmail": {
                    "type": "string"
                  },
                  "domainRegPassword": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "text": {
                      "description": "Confirmation message.",
                      "type": "string"
                    },
                    "ticket": {
                      "description": "The support ticket ID created for tracking the migration. Use this with `/tickets/{id}` to check migration progress.",
                      "type": "integer"
                    }
                  }
                }
              }
            },
            "description": "Migration request submitted."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postWebsiteMigration",
        "summary": "Submit a request for InterServer staff to migrate a website from another host",
        "description": "Submits a migration request: opens a support ticket containing the customer's credentials for their current host (cPanel/FTP/domain registrar) so InterServer staff can copy the site, databases, and email into this webhosting account. **Sensitive** — the body contains plaintext credentials for the source host. Do not log responses. The created ticket's id is returned; track progress with the helpdesk/tickets API. Sibling ops: `getWebsiteInfo`, `getWebsitesBackups`.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body fields** (JSON or multipart):\n- `custPortal` (string) — URL of the current hosting provider's customer portal (e.g. `sso.godaddy.com`).\n- `regEmail` (string) — email/username at the current host.\n- `password` (string) — password at the current host.\n- `ctrlPanel` (string) — current control-panel URL (e.g. `yourdomain.com/cpanel/`).\n- `ftpUsername` (string), `ftpPassword` (string) — FTP credentials.\n- `siteBusyMig` (string) — info on site traffic / whether a holding page can be shown during migration.\n- `splReqMig` (string) — special requirements (PHP version, modules, etc.).\n- `domainReg` (string) — whether domain-registration transfer is also needed (`yes`/`no` or freeform).\n- `dataMig` (string) — nameserver switch timing preference.\n- `domainRegPortal`, `domainRegEmail`, `domainRegPassword` (strings) — domain-registrar credentials.\n\n**Returns:** `{text: \"Your migration request has been sucessfully submitted...\", ticket: <integer>}` — pass `ticket` to the tickets API to monitor.\n\n**Side effects:**\n- Creates a support ticket via `create_ticket()` with the credentials in the ticket body.\n- Inserts a `history_log` row of type `Webhost Migration`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Service Passed` — `id` not owned by caller.\n\n**Related calls:**\n- **Track migration progress:** Tickets API (use the returned `ticket` id).\n- **Verify after migration:** `getWebsitesLogin`, `getWebsiteInfo`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The website service ID. Use `website_id` from `GET /websites`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/websites/{id}/reverse_dns": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReverseDnsEntries"
                }
              }
            },
            "description": "List of reverse dns entries"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "gettWebsiteReverseDns",
        "summary": "Read current reverse-DNS (PTR) records for the website's IPs",
        "description": "Returns the current PTR/reverse-DNS hostname for every IP attached to the website — primary `website_ip` plus any addons (from `get_service_addons().extra_ips`). PTRs are read live via `get_hostname()`, not cached. Use to render a PTR editor before calling `postWebsitesReverseDns`. **Note:** the operationId has a typo (`gettWebsiteReverseDns` with double-t) preserved for back-compat — do not rename. Sibling ops: `postWebsitesReverseDns` (update), `getWebsiteBuyIp` (broader IP+billing view), `postWebsiteBuyIp` (also supports `action=reverse_dns`).\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** None.\n\n**Returns:** `ReverseDnsEntries` — `{\"ips\": {\"<ip>\": \"<ptr-hostname>\", ...}}`. Empty string for IPs with no PTR set.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` (legacy text) — `id` not owned by caller.\n- `409 Website is not active` — `website_status != \"active\"`.\n\n**Related calls:**\n- **Update PTRs:** `postWebsitesReverseDns`.\n- **Add IPs first:** `getWebsiteBuyIp` → `postWebsiteBuyIp`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextResponse"
                }
              }
            },
            "description": "Response from the update reverse DNS call."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postWebsitesReverseDns",
        "summary": "Bulk-update reverse-DNS (PTR) records for one or more website IPs",
        "description": "Sets the PTR hostname for each IP in the website's IP set. Calls `reverse_dns($ip, $newHostname)` for every IP in the body whose value differs from the current PTR and is non-empty; IPs not in the body are left alone. Always returns `{message: \"DNS Updated\", success: true}` even if no entries actually changed. PTR propagation is asynchronous — re-call `gettWebsiteReverseDns` after a few minutes to confirm. Equivalent to calling `postWebsiteBuyIp` with `action=reverse_dns`. Sibling ops: `gettWebsiteReverseDns`, `getWebsiteBuyIp`, `postWebsiteBuyIp`.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body fields** (schema `ReverseDnsEntries`):\n- `ips` (object, required) — `{\"<ip>\": \"<new-hostname>\", ...}`. Only IPs that already belong to the website are updated; others ignored. Empty-string values skipped.\n\n**Returns:** `{message: \"DNS Updated\", success: true}`.\n\n**Side effects:**\n- One `reverse_dns()` call per IP whose value changed. Records are written to the in-addr.arpa zone; TTL-dependent propagation.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed` (legacy text) — `id` not owned by caller.\n- `409 Website is not active` — `website_status != \"active\"`.\n\n**Related calls:**\n- **Read current PTRs first:** `gettWebsiteReverseDns`.\n- **Equivalent endpoint:** `postWebsiteBuyIp` (`action=reverse_dns`).\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The website service ID. Use `website_id` from `GET /websites`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/websites/{id}/welcome_email": {
      "get": {
        "tags": [
          "Webhosting"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getWebsitesWelcomeEmail",
        "summary": "Resend the webhosting welcome email with control-panel credentials and URL",
        "description": "Resends the webhosting welcome email — the new-account email containing control-panel hostname, username, password, and getting-started instructions. Calls the dynamically-resolved `website_welcome_email($id)` helper which composes and dispatches the message to the account's `account_lid`. Idempotent — safe to call multiple times. Use after `addWebsite` finishes provisioning, or whenever a customer reports losing the original. Sibling welcome-email endpoints in other modules: `getVpsWelcomeEmail`, `getDomainsWelcomeEmail`, `getMailWelcomeEmail`. For an auto-login URL (no password reveal), use `getWebsitesLogin` instead.\n\n**Path param:**\n- `id` (integer, required) — `website_id` from `getWebsiteList`.\n\n**Body:** None.\n\n**Returns:** `SuccessTextResponse` — `{text: \"Welcome Email has been resent.\"}`.\n\n**Side effects:**\n- Sends an email to the account's billing email address with the control-panel credentials currently stored in `history_log` for this website.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Service Passed` — `id` not owned by caller.\n- `409 Service is not active` — `website_status != \"active\"`.\n\n**Related calls:**\n- **Auto-login instead:** `getWebsitesLogin` (one-time URL, no password disclosure).\n- **List sites first:** `getWebsiteList`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The website service ID. Use `website_id` from `GET /websites`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/advsend": {
      "summary": "Sends an Email with Advanced Options",
      "description": "Sends An email through one of your mail orders allowing additional options such as file attachments, cc, bcc, etc.",
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMailAdv"
              },
              "examples": {
                "AdvSendExample": {
                  "value": {
                    "subject": "Welcome",
                    "body": "Hello",
                    "from": {
                      "email": "user@domain.com"
                    },
                    "to": [
                      {
                        "email": "someone@client.com",
                        "name": "Mr Client"
                      }
                    ],
                    "attachments": [
                      {
                        "filename": "message.txt",
                        "data": "base64_encoded_contents"
                      }
                    ],
                    "id": 66
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SendMailAdv"
              },
              "examples": {
                "AdvSendExample": {
                  "value": {
                    "subject": "Welcome",
                    "body": "Hello",
                    "from": {
                      "email": "user@domain.com"
                    },
                    "to": [
                      {
                        "email": "someone@client.com",
                        "name": "Mr Client"
                      }
                    ],
                    "attachments": [
                      {
                        "filename": "message.txt",
                        "data": "base64_encoded_contents"
                      }
                    ],
                    "id": 66
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "description": "Email queued successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "sendAdvMail",
        "summary": "Send email via Mail Baby SMTP relay with attachments, CC/BCC, and multi-recipient",
        "description": "Submits an outbound message through `relay.mailbaby.net:25` using the service's SMTP credentials (fetched via `mail_get_password`). Use for multi-recipient sends, named addresses, CC/BCC, ReplyTo, or attachments. For single-recipient plain sends, `sendMail` is the lighter option. Sibling ops: `sendMail`, `viewMailLog` (find queued message), `getMailDeliverability` (analyze bounces).\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields (JSON or form-urlencoded, schema `SendMailAdv`):**\n- `from` (string or `{email, name}`, required).\n- `to` (array of strings or `{email, name}` objects, required).\n- `subject` (string, required).\n- `body` (string, required) — HTML auto-detected when tags are present.\n- `replyto` (array, optional) — same shape as `to`.\n- `cc`, `bcc` (array, optional) — same shape as `to`.\n- `attachments` (array, optional) — each `{filename, data}` where `data` is base64-encoded; added via `addStringAttachment`.\n\n**Returns:** `{status: \"ok\", text: \"Email queued successfully\"}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `400` with PHPMailer `ErrorInfo` on send failure or missing required field.\n- `401` — unauthenticated.\n- `404 Invalid Service Passed`.\n- `409 Service is not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/blocks/delete": {
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/EmailAddress"
              },
              "examples": {
                "BlocksDeleteExampleForm": {
                  "value": {
                    "email": "client@domain.com"
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailAddress"
              },
              "examples": {
                "BlocksDeleteExample": {
                  "value": {
                    "email": "client@domain.com"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "description": "Email address removed from block list successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "delistBlock",
        "summary": "Delist a sender email from rspamd / mailchannels / mailbaby block lists",
        "description": "Removes block rows for the supplied email across the three reputation stores: `rspamd` (by `fromemail`), `mailchannels` (by `email`), `mailbaby` (by `emailfrom`). Functionally equivalent to `postMailDelist` but uses `email` parameter naming and returns 400 (not error JSON) for an invalid address. Sibling ops: `getMailBlocks`, `getMailDelist`, `postMailDelist`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields (schema `EmailAddress`):**\n- `email` (string, required) — sender address; validated via `FILTER_VALIDATE_EMAIL`.\n\n**Returns:** `{status: \"ok\", text: \"Email '...' removed from block list\"}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `400` invalid email, `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/rules": {
      "summary": "Deny Rules",
      "description": "The deny rules allow you to setup assorted types of rules which will automatically deny the email from being sent through our system when  the citeria match.",
      "get": {
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DenyRuleRecord"
                  }
                },
                "examples": {
                  "ExampleMailRulesList": {
                    "value": [
                      {
                        "id": 14,
                        "user": "mb20682",
                        "type": "email",
                        "data": "domeinwo@server.guesshost.net",
                        "created": "2022-03-22 19:16:35"
                      },
                      {
                        "id": 15,
                        "user": "mb20682",
                        "type": "email",
                        "data": "mamediieva@thedigital.gov.ua",
                        "created": "2022-03-22 19:18:01"
                      }
                    ]
                  }
                }
              }
            },
            "links": {
              "deleteRuleLink": {
                "operationId": "deleteRule",
                "parameters": {
                  "id": "$request.path.id",
                  "rule": "$response.body#/0/id"
                },
                "description": "The `id` value returned in each rule can be used as the `rule` parameter in `DELETE /mail/{id}/rules/{rule}` to remove that rule."
              }
            },
            "description": "List of configured deny rules."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getRules",
        "summary": "List configured deny rules (sender/recipient blocks) for a Mail Baby service",
        "description": "Returns every `mail_spam` row scoped to this service's `mail_username` — local sender/recipient block rules the customer has configured. Sibling ops: `addRule`, `updateRule`, `deleteRule`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Returns:** Array of `DenyRuleRecord` — `{id, user, type, data, created}`. `type` values:\n- `domain` — block by sender domain.\n- `email` — block by exact sender email.\n- `startswith` — block when sender local-part starts with a string.\n- `destination` — block by recipient email.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `401`, `404`, `409 not active`.\n"
      },
      "post": {
        "requestBody": {
          "description": "These are the fields needed to create a new email deny rule.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DenyRuleNew"
              },
              "examples": {
                "DenyRuleNewJsonExample": {
                  "value": {
                    "user": "mb20682",
                    "type": "email",
                    "data": "domeinwo@server.guesshost.net"
                  }
                }
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/DenyRuleNew"
              },
              "examples": {
                "DenyRuleNewFormExample": {
                  "value": {
                    "user": "mb20682",
                    "type": "email",
                    "data": "domeinwo@server.guesshost.net"
                  }
                }
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "description": "Deny rule created successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "addRule",
        "summary": "Create a new deny rule to auto-block matching submissions",
        "description": "Inserts a new `mail_spam` row scoped to this service's `mail_username` so the relay drops matching submissions. Sibling ops: `getRules`, `updateRule`, `deleteRule`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields (schema `DenyRuleNew`):**\n- `type` (string, required) — `domain` / `email` / `startswith` / `destination`.\n- `data` (string, required) — literal value matched; validation: no quotes, valid domain for `type=domain`, valid email for `type=email`, `[A-Z0-9+_.-]+` for `startswith`.\n\n**Returns:** `\"Spam Block Added\"`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** field-level errors on validation failure, `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/rules/{rule}": {
      "put": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DenyRuleNew"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DenyRuleNew"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "parameters": [
          {
            "name": "rule",
            "description": "The ID of the deny rule to update.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "description": "Deny rule updated successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "updateRule",
        "summary": "Update an existing Mail Baby deny rule's type and match data",
        "description": "Updates `type` and `data` on a single `mail_spam` row. Query is bounded by `id={rule} AND user='{mail_username}'` so cross-tenant updates are impossible. Same validation rules as `addRule`. Sibling ops: `getRules`, `addRule`, `deleteRule`.\n\n**Path params:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n- `rule` (string, required) — rule id from `getRules`.\n\n**Body fields (schema `DenyRuleNew`):**\n- `type` (string, required) — `domain` / `email` / `startswith` / `destination`.\n- `data` (string, required) — see `addRule` for type-specific validation.\n\n**Returns:** `\"Record updated successfully.\"`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** field-level errors on validation failure, `401`, `404`, `409 not active`.\n"
      },
      "delete": {
        "tags": [
          "Mail"
        ],
        "parameters": [
          {
            "examples": {
              "RuleIdExample": {
                "value": "34"
              }
            },
            "name": "rule",
            "description": "The ID of the Rules entry.",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "description": "Deny rule deleted successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "deleteRule",
        "summary": "Delete a Mail Baby deny rule by rule ID (hard delete — no recovery)",
        "description": "Hard-deletes a single `mail_spam` row scoped to this service's `mail_username`. **Irreversible** — no audit copy preserved. Query filter `id={rule} AND user='{mail_username}'` prevents cross-tenant deletes; passing a `rule` belonging to a different mail order is silently a no-op (still returns success). Sibling ops: `getRules`, `addRule`, `updateRule`.\n\n**Path params:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n- `rule` (string, required) — rule id from `getRules`.\n\n**Returns:** `\"Block deleted successfully.\"`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/send": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMail"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/SendMail"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Mail"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GenericResponse"
                }
              }
            },
            "description": "Email queued successfully."
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "operationId": "sendMail",
        "summary": "Send a simple single-recipient email through the Mail Baby SMTP relay",
        "description": "Sends a single-recipient transactional email through `relay.mailbaby.net:25` authenticated as this `mail_id`. Body fields are the minimum needed for a plain send; Reply-To is auto-set to `from`. For multi-recipient sends, CC/BCC, named addresses, or attachments use `sendAdvMail` instead. Sibling ops: `sendAdvMail`, `viewMailLog`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Body fields (JSON or form-urlencoded, schema `SendMail`):**\n- `to` (string, required) — recipient email.\n- `from` (string, required) — sender email.\n- `subject` (string, required).\n- `body` (string, required) — HTML auto-detected when tags are present.\n\n**Returns:** `{status: \"ok\", text: \"Email queued successfully\"}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `400` with PHPMailer `ErrorInfo` on send failure or missing required field, `401`, `404`, `409 not active`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/stats": {
      "get": {
        "tags": [
          "Mail"
        ],
        "parameters": [
          {
            "name": "time",
            "description": "The timeframe for the statistics.",
            "schema": {
              "enum": [
                "all",
                "billing",
                "month",
                "7d",
                "24h",
                "1d",
                "1h"
              ],
              "type": "string"
            },
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailStatsType"
                }
              }
            },
            "links": {
              "getMailLog": {
                "operationId": "viewMailLog",
                "parameters": {
                  "id": "$request.path.id"
                },
                "description": "Use `GET /mail/{id}/log` to view detailed message-level logs for this mail service."
              }
            },
            "description": "Mail service usage statistics."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getStats",
        "summary": "Read Mail Baby usage counts, send volume totals, top destinations, and projected cost",
        "description": "Returns aggregate usage and cost metrics for the SMTP user behind `mail_id` from the ZoneMTA `mail_messagestore` / `mail_senderdelivered` tables. Use to drive an analytics dashboard or to project end-of-cycle cost. Sibling ops: `viewMailLog`, `getMailDeliverability`.\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList`.\n\n**Query params:**\n- `time` (string enum, optional, default `1h`) — window: `all` / `billing` (current invoice cycle) / `month` / `7d` / `24h` / `1d` / `1h`.\n\n**Returns** (schema `MailStatsType`):\n- `time` (string) — echo of selected window.\n- `usage` (integer) — full-billing-cycle send count.\n- `currency`, `currencySymbol` (string).\n- `cost` (decimal) — projected = base + `$0.20 / 1000 emails`.\n- `received`, `sent` (integer).\n- `volume.to`, `volume.from`, `volume.ip` (object) — top-500 destinations / senders / origin IPs by count.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `Invalid or missing mail order id`, `401`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/account/timezones": {
      "get": {
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "TimezonesExample": {
                    "value": [
                      "Africa/Abidjan",
                      "America/New_York",
                      "Asia/Tokyo",
                      "Europe/London",
                      "UTC"
                    ]
                  }
                }
              }
            },
            "description": "An array of all available timezone identifiers."
          }
        },
        "security": [
          {}
        ],
        "operationId": "getTimezones",
        "summary": "List all PHP timezone identifiers usable on accounts and services",
        "description": "Populates a timezone picker for account preferences or for VPS / QuickServer timezone changes. Public — no auth required. Backed by PHP's `DateTimeZone::listIdentifiers()` so the catalog is large (~400+ zones, including deprecated aliases like `US/Eastern`). Result is fixed for a given PHP build — cache aggressively client-side. Sibling ops: `postVpsChangeTimezone`, `postQsChangeTimezone`, `getCountries`, `getAccountLocales`, `getAccountCurrencies`.\n\n**Path/Query/Body:** None.\n\n**Returns:** flat JSON array of stable IANA tz strings, e.g. `[\"Africa/Abidjan\", \"America/New_York\", \"Asia/Tokyo\", \"Europe/London\", \"UTC\"]`. Values are usable verbatim on the timezone-change endpoints; no translation or country-grouping is performed here.\n\n**Auth:** None.\n\n**Errors:** No documented error path under normal operation.\n\n**Related calls:**\n- **Apply selection to a service:** `postVpsChangeTimezone` (`/vps/{id}/change_timezone`), `postQsChangeTimezone` (`/qs/{id}/change_timezone`).\n- **Apply to account profile:** `updateAccountInfo` (sets `timezone`).\n- **Other preference catalogs:** `getCountries`, `getAccountLocales`, `getAccountCurrencies`.\n"
      }
    },
    "/account/countries": {
      "get": {
        "tags": [
          "Public"
        ],
        "parameters": [
          {
            "name": "fetch_by",
            "description": "Get countries by iso2 or iso3 or numcode",
            "schema": {
              "enum": [
                "iso2",
                "iso3",
                "numcode"
              ],
              "type": "string"
            },
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "examples": {
                  "AccountCountriesExample": {
                    "value": {
                      "AF": "Afghanistan",
                      "US": "United States",
                      "ZW": "Zimbabwe"
                    }
                  }
                }
              }
            },
            "description": "The list of countries with both 2 letter abbreviations and the country names."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "getCountries",
        "summary": "List enabled countries keyed by ISO-2/ISO-3/numeric code",
        "description": "Populates country dropdowns in account registration, billing-address forms, and domain/whois contact forms. Public — no auth required. Disabled countries (e.g. embargoed jurisdictions) are excluded — admins toggle this in `country_t.enabled`. Sibling ops: `getTimezones`, `getAccountLocales`, `getAccountCurrencies`, `updateAccountInfo` (consumes the chosen country).\n\n**Query parameters:**\n- `fetch_by` (string, optional) — one of `iso2` (default; two-letter codes like `US`, `GB`), `iso3` (three-letter like `USA`, `GBR`), or `numcode` (UN M49 numeric like `840`). Any other value silently falls back to `iso2`.\n\n**Body:** None.\n\n**Returns:** JSON object mapping the chosen key format to the country's short name — e.g. `{ \"AF\": \"Afghanistan\", \"US\": \"United States\", \"ZW\": \"Zimbabwe\" }`. Sourced from the `country_t` table, filtered to `enabled=1`, ordered alphabetically by `short_name`.\n\n**Auth:** None.\n\n**Errors:** No documented error path.\n\n**Related calls:**\n- **Apply to account profile:** `updateAccountInfo`.\n- **Other preference catalogs:** `getTimezones`, `getAccountLocales`, `getAccountCurrencies`.\n"
      }
    },
    "/account/locales": {
      "get": {
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "description": "English display name of the locale.",
                        "type": "string"
                      },
                      "local_name": {
                        "description": "Display name of the locale in its own language.",
                        "type": "string"
                      }
                    }
                  }
                },
                "examples": {
                  "AccountLocalesExample": {
                    "value": {
                      "en": {
                        "name": "English",
                        "local_name": "English"
                      },
                      "es": {
                        "name": "Spanish",
                        "local_name": "español"
                      },
                      "fr": {
                        "name": "French",
                        "local_name": "français"
                      }
                    }
                  }
                }
              }
            },
            "description": "Map of locale identifiers to display names."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "getAccountLocales",
        "summary": "List supported UI locales with English and native display names",
        "description": "Renders the language-picker for account preferences or login pages. Public — no auth required. Cross-references PHP's Punic locale data with `locale/google_langs.php` so only locales with Google Translate support are returned. Result is essentially static — cache client-side. Sibling ops: `getCountries`, `getTimezones`, `getAccountCurrencies`, `updateAccountInfo` (consumes the chosen locale).\n\n**Path/Query/Body:** None.\n\n**Returns:** JSON object keyed by BCP-47-style locale code, with `{ name, local_name }` per entry — e.g.\n\n    {\n      \"en\": { \"name\": \"English\", \"local_name\": \"English\" },\n      \"es\": { \"name\": \"Spanish\", \"local_name\": \"español\" },\n      \"fr\": { \"name\": \"French\", \"local_name\": \"français\" }\n    }\n\n`name` is the English label; `local_name` is the locale's name in its own language (good for accessibility and avoiding the wrong-script problem).\n\n**Auth:** None.\n\n**Errors:** No documented error path.\n\n**Related calls:**\n- **Apply to account profile:** `updateAccountInfo` (sets `locale`).\n- **Other preference catalogs:** `getCountries`, `getTimezones`, `getAccountCurrencies`.\n"
      }
    },
    "/account/currencies": {
      "get": {
        "tags": [
          "Public"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "examples": {
                  "AccountCurrenciesExample": {
                    "value": [
                      "USD",
                      "EUR",
                      "GBP",
                      "INR"
                    ]
                  }
                }
              }
            },
            "description": "An array of enabled currency codes."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "security": [
          {}
        ],
        "operationId": "getAccountCurrencies",
        "summary": "List enabled currency codes accepted for billing and preferences",
        "description": "Populates a currency selector on signup, billing-preferences, or invoice-display forms. Public — no auth required. The list changes only when an admin enables/disables a currency — cache client-side. Sibling ops: `getCountries`, `getTimezones`, `getAccountLocales`, plus the billing-preference endpoints under `/account/*` and `/billing/*`.\n\n**Path/Query/Body:** None.\n\n**Returns:** flat JSON array of ISO-4217 currency codes — e.g. `[\"USD\", \"EUR\", \"GBP\", \"INR\"]`. Sourced from rows in the `currencies` table where `currency_enabled=1`, in the natural row order. The endpoint returns codes only — for symbols, decimals, or exchange rates use a separate currency-detail endpoint or a static client-side map.\n\n**Auth:** None.\n\n**Errors:** No documented error path.\n\n**Related calls:**\n- **Apply to account profile:** `updateAccountInfo`.\n- **Other preference catalogs:** `getCountries`, `getTimezones`, `getAccountLocales`.\n"
      }
    },
    "/servers/{id}/ipmi_power": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "serverIpmiPowerGet",
        "summary": "Read IPMI chassis power status for a dedicated server (single)",
        "description": "Use to check whether a server's chassis is currently `on`/`off` via IPMI before issuing a power action. Path param: `id` (integer server_id). Optional body `asset` (asset_id — defaults to first asset). Issues `ipmitool power status` against the asset's `ipmi_ip` using its location IPMI group/credentials.\nReturns: `{ text:'Chassis Power is on' }` (or 'off'). Errors: 404 if `id` not owned by caller; 409 if service not active; 'There was an error sending the IPMI command' if BMC unreachable. Caveat: BMCs occasionally rate-limit — back off on repeated errors.\nSibling ops: `serverBulkIpmiPowerGet` (preferred when polling many servers — single round-trip), `serverIpmiPowerPost` (DESTRUCTIVE — change power), `getServerInfo` (full state), `serverIpmiLiveGet` (IPMI Live KVM)."
      },
      "post": {
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ServerIpmiPowerRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServerIpmiPowerRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          }
        },
        "operationId": "serverIpmiPowerPost",
        "summary": "DESTRUCTIVE — change chassis power state on a bare-metal server",
        "description": "Sends an IPMI chassis power command (`on`, `off`, `cycle`, `reset`, `soft`) to a customer's physical dedicated server. **DESTRUCTIVE on running hardware:** `off` / `cycle` / `reset` are forced power events that can corrupt filesystems, lose un-flushed data, or break in-flight workloads. `soft` requests an ACPI shutdown (safer when the guest OS is responsive). Always confirm intent with the operator. Sibling ops: `serverIpmiPowerGet` (read first), `serverBulkIpmiPowerGet` (status only), `serverIpmiLivePost` (KVM access).\n\n**Path:** `id` (integer, required) — server_id.\n\n**Body fields:**\n- `action` (string, required) — one of `on` / `off` / `cycle` / `reset` / `soft`.\n- `asset` (integer, optional) — asset_id; defaults to first asset on the server.\n\n**Returns:** `{ text: 'Power command sent. Response: <ipmi output>' }`.\n\n**Auth:** Session/API key. Ownership enforced via `server_custid`.\n\n**Errors:**\n- `422` / inline error text — `Invalid Action` when `action` is not in the allowed set.\n- `404` — `id` not owned, or `asset` not on this server.\n- `409` — service not `active`.\n- `200` with error text — `'There was an error sending the IPMI command.'` when BMC is unreachable or rate-limiting.\n\n**Related calls:**\n- **Status (single / bulk):** `serverIpmiPowerGet`, `serverBulkIpmiPowerGet`.\n- **KVM console:** `serverIpmiLivePost`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "Server ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/servers/bulk/ipmi_power": {
      "get": {
        "tags": [
          "Servers"
        ],
        "parameters": [
          {
            "name": "ids",
            "description": "Comma-separated list of Server IDs to query (e.g. `2313,2314,2315`). May also be passed as repeated `ids[]` query parameters.",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": true,
            "example": "2313,2314,2315"
          }
        ],
        "responses": {
          "200": {
            "description": "Bulk IPMI power status response, one entry per requested server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerBulkIpmiPowerResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadInput"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "serverBulkIpmiPowerGet",
        "summary": "Read IPMI chassis power status for many dedicated servers in one call",
        "description": "Use when you need power status for several owned servers at once (dashboards, mass health checks). Each server is queried independently; per-server failures (invalid id, inactive service, no asset, BMC error) are reported in the same response without aborting the batch. Read-only — does NOT change power state.\nQuery: `ids` (required) — comma-separated string `?ids=2313,2314,2315` OR repeated `ids[]` array. Duplicates de-duped; non-positive ints become per-row errors. Returns: `{ results: [ { id, asset?, text|error } ] }`. Errors: 400 'No server IDs provided.' if `ids` empty/missing; 401 unauth.\nSibling ops: `serverIpmiPowerGet` (single-server equivalent), `serverIpmiPowerPost` (DESTRUCTIVE — change power; no bulk equivalent — call per server), `getServerList` (discover ids)."
      }
    },
    "/qs/{id}/welcome_email": {
      "get": {
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getQsWelcomeEmail",
        "summary": "Resend the QuickServer welcome email with login credentials",
        "description": "Re-runs the `qs_welcome_email` function which composes and sends the welcome email containing connection details, root password, and management URLs to the account owner. Path param: `id` (integer).\nReturns: `{ text: \"Welcome Email has been resent.\" }`. Errors: 401, 404 if not owned by caller, 409 if status != `active`. Use when the original welcome email was lost or the customer needs credentials again.\nSiblings: `getVpsWelcomeEmail`, `getQsInfo` (also exposes connection info)."
      },
      "parameters": [
        {
          "examples": {
            "websiteIdExample": {
              "value": "133123"
            }
          },
          "name": "id",
          "description": "Quickserver ID",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/welcome_email": {
      "get": {
        "tags": [
          "VPS"
        ],
        "parameters": [
          {
            "examples": {
              "vpsIdExample": {
                "value": "4813"
              }
            },
            "name": "id",
            "description": "VPS ID",
            "schema": {
              "type": "string"
            },
            "in": "path",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/SuccessTextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getVpsWelcomeEmail",
        "summary": "Resend the welcome email containing VPS IP, hostname, and root credentials",
        "description": "Resends the VPS welcome email — the original new-service email containing IP, hostname, root/Administrator credentials, and connection instructions. Calls `vps_welcome_email($id)` to regenerate and dispatch via the standard mail pipeline. Use when the customer didn't receive (or lost) the original right after provisioning. The dashboard's \"show credentials\" view is the alternative for in-app retrieval. Sibling ops: `getVpsInfo` (shows connection details in the response), `postVpsChangeRootPassword` / `postVpsResetPassword` (rotate before resending if security is a concern).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body:** None.\n\n**Returns:** `SuccessTextResponse` — `Welcome Email has been resent.`\n\n**Side effects:**\n- Sends an email to the account's billing email address with the credentials currently stored in `history_log` for this VPS.\n\n**Auth:** Session/API key. Ownership enforced via `vps_custid`.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid Service Passed` — `id` not owned by caller.\n- `409 Service is not active` — `vps_status != \"active\"`.\n\n**Related calls:**\n- **In-app credential view:** `getVpsInfo`.\n- **Before resending, rotate password:** `postVpsResetPassword` or `postVpsChangeRootPassword`.\n"
      },
      "parameters": [
        {
          "examples": {
            "vpsIdExample": {
              "value": "4142"
            }
          },
          "name": "id",
          "description": "VPS ID",
          "schema": {
            "type": "string"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/servers/{id}/reverse_dns": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReverseDnsEntries"
                }
              }
            },
            "description": "Servers Reverse DNS info response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getServerReverseDns",
        "summary": "List current reverse-DNS (PTR) records for a dedicated server's IPs",
        "description": "Use to read the existing PTR/rDNS hostnames assigned to each public IP in the server's VLANs — typically before calling `postServerReverseDns` to update them. Path param: `id` (integer server_id). No body. Walks `networkInfo.vlans`, expands each network to usable host IPs (handles /31 and /32 edge cases), and resolves each via `get_hostname()`.\nReturns: `{ ips: { '<ipv4>': '<ptr_or_empty_string>', ... } }`. Empty string indicates no PTR set. Errors: 404 if `id` not owned by caller; 401 unauth.\nSibling ops: `postServerReverseDns` (update PTRs), `getServerInfo` (full network), `getVpsReverseDns` for VPS, `getDomainNameservers` / DNS endpoints for forward records. Note rDNS propagation is delegated to the in-addr.arpa zone — changes are not always instant."
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/ReverseDnsEntries"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextResponse"
                }
              }
            },
            "description": "Update Server Reverse DNS response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postServerReverseDns",
        "summary": "Update reverse-DNS (PTR) hostnames on a dedicated server's IPs",
        "description": "Use to set or remove PTR records for the server's public IPs. Path param: `id` (server_id). Body: `ips` (object mapping `'<ipv4>'` to desired hostname; empty string removes the PTR). Only IPs that already exist on the server's VLANs and whose hostname differs from current are updated; each diff calls `reverse_dns($ip, $host, 'set_reverse'|'remove_reverse')`.\nReturns: `{ message, success:bool }`. `success:false` with 'No valid IPs were passed or there were no changes' when nothing to update; otherwise reports update count. Errors: 404 invalid id; 401 unauth. Caveats: caller can only set PTRs for IPs they actually own; rDNS propagation is async — do not assume immediate visibility downstream.\nSibling ops: `getServerReverseDns` (read first), `getServerInfo`, VPS counterpart `postVpsReverseDns`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "Server ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/mail/{id}/log": {
      "get": {
        "tags": [
          "Mail"
        ],
        "parameters": [
          {
            "example": 2604,
            "name": "id",
            "description": "The numeric ID of the mail order to filter by.  When omitted, logs from the first active mail order are returned.  Obtain valid IDs from `GET /mail` or `GET /mail/{id}`.",
            "schema": {
              "format": "int64",
              "type": "integer"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "1.2.3.4",
            "name": "origin",
            "description": "Filter by the originating IP address from which the message was submitted to the relay.  Must be a valid IPv4 or IPv6 address.",
            "schema": {
              "format": "ipv4",
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "mx.google.com",
            "name": "mx",
            "description": "Filter by the MX hostname the relay attempted delivery to.  For example `mx.google.com` would return messages destined for Gmail recipients. Maps to `mxHostname` in the `MailLogEntry` response.",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "me@sender.com",
            "name": "from",
            "description": "Filter by SMTP envelope `MAIL FROM` address (exact match).  This is the address the relay used for bounce handling and may differ from the `From:` message header.  For header-level filtering use `headerfrom`.",
            "schema": {
              "format": "email",
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "you@receiver.com",
            "name": "to",
            "description": "Filter by SMTP envelope `RCPT TO` address (exact match).  This is the delivery address used by the relay and may differ from the `To:` header when BCC recipients are involved.",
            "schema": {
              "format": "email",
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "Your order has shipped",
            "name": "subject",
            "description": "Filter by email `Subject` header (exact match).  MIME-encoded subjects are decoded automatically in the response.",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "185997065c60008840",
            "name": "mailid",
            "description": "Filter by the relay-assigned mail ID string (exact match).  This corresponds to the `id` field in `MailLogEntry` and to the `text` value returned by the sending endpoints on success.  Format is an 18-19 character hexadecimal string such as `185997065c60008840`.",
            "schema": {
              "maxLength": 19,
              "minLength": 18,
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "<abc123@yourdomain.com>",
            "name": "messageId",
            "description": "Filter by the `Message-ID` email header using a substring (case-insensitive) match. The `Message-ID` is assigned by the sending mail client and is visible in the `messageId` field of `MailLogEntry`.",
            "schema": {
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "replies@sender.com",
            "name": "replyto",
            "description": "Filter by the `Reply-To` message header address (exact match).  Only returns messages where this header was explicitly set.",
            "schema": {
              "format": "email",
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "newsletter@sender.com",
            "name": "headerfrom",
            "description": "Filter by the `From` message header address (exact match).  This is the human-visible sender address and may differ from the SMTP envelope `from` parameter when sending on behalf of another address.",
            "schema": {
              "format": "email",
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": 1,
            "name": "delivered",
            "description": "Filter by delivery status.  `1` returns only messages that were successfully delivered to the destination MX.  `0` returns messages that are still queued, deferred, or failed.  Omit to return all messages regardless of delivery status.",
            "schema": {
              "title": "DeliveredStatus",
              "enum": [
                0,
                1
              ],
              "type": "integer"
            },
            "in": "query",
            "required": false
          },
          {
            "example": 0,
            "name": "skip",
            "description": "Number of records to skip for pagination.  Use in combination with `limit` to page through large result sets.  Defaults to `0` (no skip).",
            "schema": {
              "format": "int32",
              "default": 0,
              "minimum": 0,
              "type": "integer"
            },
            "in": "query",
            "required": false
          },
          {
            "example": 100,
            "name": "limit",
            "description": "Maximum number of records to return per page.  Defaults to `100`. Maximum allowed value is `10000`.  The response also includes a `total` field with the full matched count so you can calculate the number of pages.",
            "schema": {
              "format": "int32",
              "default": 100,
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            },
            "in": "query",
            "required": false
          },
          {
            "example": 1641781008,
            "name": "startDate",
            "description": "Earliest date to include.  Accepts either a Unix timestamp (integer seconds since epoch) or a date string parseable by `strtotime()` such as `2024-01-15` or `last monday`.  Messages with a `time` value **greater than or equal to** this value will be included.",
            "schema": {
              "oneOf": [
                {
                  "format": "int64",
                  "maximum": 9999999999,
                  "minimum": 0,
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "in": "query",
            "required": false
          },
          {
            "example": 1673317008,
            "name": "endDate",
            "description": "Latest date to include.  Accepts either a Unix timestamp (integer seconds since epoch) or a date string parseable by `strtotime()` such as `2024-01-31` or `yesterday`. Messages with a `time` value **less than or equal to** this value will be included.",
            "schema": {
              "oneOf": [
                {
                  "format": "int64",
                  "maximum": 9999999999,
                  "minimum": 0,
                  "type": "integer"
                },
                {
                  "type": "string"
                }
              ]
            },
            "in": "query",
            "required": false
          },
          {
            "example": "time",
            "name": "sort",
            "description": "Field to sort results by.  Currently only `time` is supported (sorts by internal row ID which corresponds to chronological order).",
            "schema": {
              "default": "time",
              "enum": [
                "time"
              ],
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "desc",
            "name": "dir",
            "description": "Sort direction.  `desc` returns newest first (default), `asc` returns oldest first.",
            "schema": {
              "default": "desc",
              "enum": [
                "asc",
                "desc"
              ],
              "type": "string"
            },
            "in": "query",
            "required": false
          },
          {
            "example": "recipient",
            "name": "groupby",
            "description": "Controls how results are grouped.  `recipient` (default) returns one row per delivery attempt — a message sent to 4 recipients produces 4 rows, each with its own `recipient`, `delivered`, `response`, and delivery metadata.  `message` collapses to one row per unique message ID; delivery-level fields will reflect one arbitrary recipient per message.  The `total` count in the response matches the grouping mode.",
            "schema": {
              "default": "recipient",
              "enum": [
                "message",
                "recipient"
              ],
              "type": "string"
            },
            "in": "query",
            "required": false
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailLog"
                },
                "examples": {
                  "MailLogRecipientExample": {
                    "summary": "groupby=recipient (default) - one row per delivery attempt",
                    "value": {
                      "total": 4,
                      "skip": 0,
                      "limit": 100,
                      "emails": [
                        {
                          "_id": 103172,
                          "id": "17c7eda538e0005d03",
                          "from": "person@mysite.com",
                          "to": "client@isp.com",
                          "subject": "sell 0.005 shares",
                          "messageId": "<vmiLEebsuCbSpUxD7oN3REpaN4VbN6BrdCAbNKIrdAo@relay0.mailbaby.net>",
                          "created": "2021-10-14 08:50:10",
                          "time": 1634215809,
                          "user": "mb5658",
                          "transtype": "ESMTPSA",
                          "origin": "199.231.189.154",
                          "interface": "feeder",
                          "sendingZone": "interserver",
                          "bodySize": 63,
                          "seq": 1,
                          "delivered": 1,
                          "code": 250,
                          "recipient": "client@isp.com",
                          "domain": "interserver.net",
                          "locked": 1,
                          "lockTime": "1634215818533",
                          "assigned": "relay1",
                          "queued": "2021-10-14T12:50:15.487Z",
                          "mxHostname": "mx.j.is.cc",
                          "response": "250 2.0.0 Ok queued as C91D83E128C"
                        }
                      ]
                    }
                  },
                  "MailLogMessageExample": {
                    "summary": "groupby=message - one row per message",
                    "value": {
                      "total": 1,
                      "skip": 0,
                      "limit": 100,
                      "emails": [
                        {
                          "_id": 103172,
                          "id": "17c7eda538e0005d03",
                          "from": "person@mysite.com",
                          "to": "client@isp.com",
                          "subject": "sell 0.005 shares",
                          "messageId": "<vmiLEebsuCbSpUxD7oN3REpaN4VbN6BrdCAbNKIrdAo@relay0.mailbaby.net>",
                          "created": "2021-10-14 08:50:10",
                          "time": 1634215809,
                          "user": "mb5658",
                          "transtype": "ESMTPSA",
                          "origin": "199.231.189.154",
                          "interface": "feeder",
                          "sendingZone": "interserver",
                          "bodySize": 63,
                          "seq": 1,
                          "delivered": 1,
                          "code": 250,
                          "recipient": "client@isp.com",
                          "domain": "interserver.net",
                          "locked": 1,
                          "lockTime": "1634215818533",
                          "assigned": "relay1",
                          "queued": "2021-10-14T12:50:15.487Z",
                          "mxHostname": "mx.j.is.cc",
                          "response": "250 2.0.0 Ok queued as C91D83E128C"
                        }
                      ]
                    }
                  }
                }
              }
            },
            "links": {
              "delistSenderBlock": {
                "operationId": "delistBlock",
                "parameters": {
                  "email": "$response.body#/emails/0/from"
                },
                "description": "A `from` address returned in the log can be passed as `email` to `POST /mail/blocks/delete` to remove a block on that sender."
              }
            },
            "description": "Paginated list of mail log entries matching the specified filters."
          },
          "400": {
            "description": "bad input parameter"
          }
        },
        "operationId": "viewMailLog",
        "summary": "Search and paginate per-message Mail Baby delivery log entries",
        "description": "Paginated search over ZoneMTA's `mail_messagestore` joined with `mail_senderdelivered` and `mail_queuerelease`. Supports envelope, header, and metadata filters; sortable; choose recipient-level or message-level grouping. Use to investigate delivery issues, find specific messages by Message-ID, audit bounce rates, or feed an analytics dashboard. Sibling ops: `getStats`, `getMailDeliverability`, `delistBlock` (clear a block surfaced by a bounce).\n\n**Path param:**\n- `id` (integer, required) — `mail_id` from `getMailList` (omit to span all owned mail users — admin-only).\n\n**Query params:**\n- `from`, `to` (string) — envelope address, exact match.\n- `headerfrom`, `replyto` (string) — header address, exact match; validated as email.\n- `subject` (string) — LIKE match on subject.\n- `mailid` (string, 18–19 chars) — relay id, exact.\n- `messageId` (string) — Message-ID header, substring match.\n- `origin` (string) — submitter IP, exact.\n- `mx` (string) — destination MX hostname, LIKE.\n- `delivered` (integer 0/1).\n- `startDate`, `endDate` (Unix timestamp or `strtotime`-parseable string).\n- `skip` (integer, default 0), `limit` (integer 1–10000, default 100).\n- `sort` (`time`), `dir` (`asc`/`desc`, default `desc`).\n- `groupby` (`recipient` default — one row per delivery attempt; `message` — one row per `_id`).\n\n**Returns** (schema `MailLog`):\n`{total, skip, limit, emails: [{id, _id, from, to, subject, messageId, time, mxHostname, delivered, code, response, recipient, ...}]}`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:** `400` bad input, `401`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The mail service ID. Use `mail_id` from `GET /mail`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/vps/{id}/restore": {
      "post": {
        "requestBody": {
          "description": "VPS Restore request",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestoreRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/RestoreRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "VPS"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "400": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postVpsRestore",
        "summary": "Restore the VPS from a backup (DESTRUCTIVE — overwrites disk)",
        "description": "**DESTRUCTIVE.** Overwrites the VPS disk with a previously created backup. Re-authenticates via the customer's MyAdmin password (when set), validates the backup row from `getVpsBackups`, checks disk capacity (skipped for ZFS), then queues `snapshot_restore` (ZFS — instant) or `restore` (Swift/MinIO — copy) on the hypervisor. Allow ~10 minutes. **Recommended pre-step:** `getVpsBackup` to snapshot current state first. Sibling ops: `getVpsBackups`, `getVpsBackup`, `postVpsReinstallOs` (wipe to fresh OS instead).\n\n**Path param:**\n- `id` (integer, required) — VPS id from `getVpsList.vps_id`.\n\n**Body fields:**\n- `backup` (string, required) — composite key `<type>:<service>:<name>` matching one of the entries from `getVpsBackups`. `type` is `swift` / `minio` / `zfs`; `service` is the originating VPS id; `name` is the backup filename.\n- `password` (string, required for non-admin callers when the account has a password set) — the customer's MyAdmin account password (re-auth check via `auth->authenticate`).\n\n**Returns:** `{ text: \"Action has been sent to the server. Please allow up to 10 minutes for action to be completed.\", queueId: <integer> }`.\n\n**Side effects:**\n- Inserts `vpsqueue` `snapshot_restore` (ZFS) or `restore` (Swift/MinIO) row.\n- Calls `vps_resetup_vnc()`.\n\n**Auth:** Session/API key plus re-auth via `password` for non-admin callers.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404 Invalid VPS Passed`.\n- `409 VPS is not active`.\n- `You do not seem to currently have a backup that you are allowed to restore yourself.` — no eligible backups found (admin-only Swift backups are excluded for non-admin callers).\n- `Invalid Password` — `password` re-auth failed.\n- `Invalid Backup Image` — `backup` doesn't match any row in `getVpsBackups`.\n- `Not Enough Space To Restore Backup. (Backup Takes up X bytes, The VPS Has Y)` — disk-size check failed (skipped for ZFS).\n\n**Related calls:**\n- **Find the backup:** `getVpsBackups` (capture `type`, `service`, `name`).\n- **Snapshot before restoring:** `getVpsBackup`.\n- **Alternative (fresh OS, no data preserved):** `postVpsReinstallOs`.\n- **Verify after restore:** `getVpsInfo`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "VPS ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/qs/{id}/restore": {
      "post": {
        "requestBody": {
          "description": "QuickServer Restore request",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RestoreRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/RestoreRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "QuickServers"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/QueueResponse"
          },
          "400": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "postQuickServerRestore",
        "summary": "Restore a QuickServer from a backup (DESTRUCTIVE — overwrites disk)",
        "description": "Overwrites the live disk with a backup. Path param: `id`. Body (form): `backup` (composite key `<type>:<service>:<name>` from `getQsBackups`), `password` (caller's account login password — required for non-admin to confirm).\nValidates backup exists, caller's password (when applicable), and that the QuickServer disk is large enough (size check skipped for ZFS). Queues `snapshot_restore` for ZFS or `restore` for swift/minio; allow up to 10 minutes. Returns: `{ text, queueId }`. Errors: 401, 404 if not owned, 409 if status != `active`, errors for invalid password, missing backup, or insufficient disk space.\nSiblings: `getQsBackups`, `getQsBackup` (create), `postVpsRestore`."
      },
      "parameters": [
        {
          "name": "id",
          "description": "QuickServer ID number",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/domains/{id}/nameservers": {
      "get": {
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DomainNameserverGetResponse"
                }
              }
            },
            "description": "Domain registered nameservers list response"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "getDomainNameservers",
        "summary": "List registered nameserver hosts and glue IP addresses for a domain",
        "description": "Returns the registered-nameserver / glue-record entries currently set for the domain at OpenSRS. **Important distinction:** these are *registered nameservers* (`host.example.com` + glue IP records the registrant has created at the registry), NOT the domain's delegation NS records. To replace the delegation set, use `updateDomainNameservers`. Empty array means no glue records are registered. Sibling ops: `addDomainNameserver`, `updateDomainNameservers`, `deleteDomainNameserver`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Returns** (schema `DomainNameserverGetResponse`):\n- Array of `{name, ipaddress, sortorder}` — use the zero-based array index when calling `deleteDomainNameserver` with `index`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n- `5xx` — registrar communication failure.\n\n**Related calls:**\n- **Add glue record:** `addDomainNameserver`.\n- **Replace delegation set:** `updateDomainNameservers`.\n- **Remove glue record:** `deleteDomainNameserver`.\n"
      },
      "put": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainNameserverPutRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DomainNameserverPutRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "updateDomainNameservers",
        "summary": "Replace the full authoritative-nameserver delegation list at the registrar",
        "description": "Atomically replaces the domain's authoritative-nameserver delegation list via OpenSRS `nsAdvancedUpdt` with `op_type=assign`. **Domain must be unlocked.** Use this to switch DNS providers (e.g. point at InterServer `cdns1`/`cdns2`, Cloudflare, AWS Route53, etc.). **Replaces the full set in one call** — partial updates are not supported through this endpoint. To register a new glue record (host + IP) at the registry, use `addDomainNameserver`. Sibling ops: `getDomainNameservers`, `addDomainNameserver`, `deleteDomainNameserver`, `addDnsDomain` (host a zone at InterServer).\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body fields (JSON or multipart, schema `DomainNameserverPutRequest`):**\n- `nameserver[]` (array of strings, required) — FQDN strings (e.g. `[\"ns1.example.com\", \"ns2.example.com\"]`). Trimmed; empty entries dropped server-side.\n\n**Returns:** `SuccessTextResponse` on registrar `is_success=1`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"` or domain locked.\n- Registrar errors surfaced as 4xx.\n\n**Related calls:**\n- **Read current set:** `getDomainNameservers`.\n- **Register a glue record:** `addDomainNameserver`.\n- **Host the DNS zone at InterServer:** `addDnsDomain` (DNS tag).\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DomainNameserverPostRequest"
              }
            },
            "multipart/form-data": {
              "schema": {
                "$ref": "#/components/schemas/DomainNameserverPostRequest"
              }
            }
          },
          "required": true
        },
        "tags": [
          "Domains"
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "addDomainNameserver",
        "summary": "Register a new nameserver host with glue IP at the registry (registered nameserver)",
        "description": "Adds a registered nameserver (host + glue IP) for the domain at the registrar via `opensrs_create_nameserver`. Use when running your own authoritative nameservers under the domain itself (e.g. `ns1.example.com`, `ns2.example.com`). **Glue records, not delegation:** this registers the host's IP at the registry; the domain's delegation must separately point at these names via `updateDomainNameservers`. Sibling ops: `getDomainNameservers`, `updateDomainNameservers`, `deleteDomainNameserver`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Body fields (schema `DomainNameserverPostRequest`):**\n- `name` (string, required) — FQDN of the nameserver (e.g. `ns1.example.com`). Validated by `valid_hostname()`.\n- `ipAddress` (string, required) — IPv4 address. Validated by `validIp()`.\n\n**Returns:** `SuccessTextResponse` on success.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n- `422` — invalid hostname or IP, or registrar refused.\n\n**Related calls:**\n- **Verify glue record:** `getDomainNameservers`.\n- **Use this nameserver in delegation:** `updateDomainNameservers`.\n"
      },
      "delete": {
        "tags": [
          "Domains"
        ],
        "parameters": [
          {
            "name": "index",
            "description": "The index of the registered nameserver from the registered nameservers list to delete.\r\n",
            "schema": {
              "type": "integer"
            },
            "in": "query",
            "required": true
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/TextResponse"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "deleteDomainNameserver",
        "summary": "Remove one registered nameserver glue record from the domain",
        "description": "Removes a single registered nameserver entry from the domain at the registrar. Handler first calls OpenSRS `nsGet` to resolve the entry by index, then `nsDelete`. Per-entry — to replace the full delegation set use `updateDomainNameservers` instead. Sibling ops: `getDomainNameservers`, `addDomainNameserver`, `updateDomainNameservers`.\n\n**Path param:**\n- `id` (integer, required) — `domain_id` from `getDomainsList`.\n\n**Query params:**\n- `index` (integer, required) — zero-based index from `getDomainNameservers` array — or alternatively pass `name` + `ip` to target by value.\n\n**Returns:** `SuccessTextResponse`.\n\n**Auth:** Session/API key. Ownership enforced.\n\n**Errors:**\n- `401` — unauthenticated.\n- `404` — `id` not owned by caller.\n- `409` — `domain_status != \"active\"`.\n- `422` — `index` out of range or `name`/`ip` not provided.\n- Registrar errors surfaced as 4xx.\n\n**Related calls:**\n- **List candidates:** `getDomainNameservers`.\n- **Replace delegation entirely:** `updateDomainNameservers`.\n"
      },
      "parameters": [
        {
          "name": "id",
          "description": "The domain service ID. Use `domain_id` from `GET /domains`.",
          "schema": {
            "type": "integer"
          },
          "in": "path",
          "required": true
        }
      ]
    },
    "/servers/order/buy_now_server": {
      "get": {
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bandwidth": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "10"
                          },
                          "short_desc": {
                            "type": "string",
                            "example": "1GBPS Unmetered"
                          },
                          "long_desc": {
                            "type": "string",
                            "example": ""
                          },
                          "monthly_price": {
                            "type": "string",
                            "example": "0"
                          }
                        }
                      }
                    },
                    "ips": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "9"
                          },
                          "short_desc": {
                            "type": "string",
                            "example": "1 Vlan Ip (/30)"
                          },
                          "long_desc": {
                            "type": "string",
                            "example": "1 IP In personal Vlan"
                          },
                          "monthly_price": {
                            "type": "string",
                            "example": "0"
                          }
                        }
                      }
                    },
                    "os": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "5"
                          },
                          "short_desc": {
                            "type": "string",
                            "example": "FreeBSD"
                          },
                          "long_desc": {
                            "type": "string",
                            "example": "Latest FreeBSD 6.x OS<br><pre>CP(s): cPanel/DirectAdmin</pre>"
                          },
                          "monthly_price": {
                            "type": "string",
                            "example": "0"
                          }
                        }
                      }
                    },
                    "cp": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "1"
                          },
                          "short_desc": {
                            "type": "string",
                            "example": "None"
                          },
                          "long_desc": {
                            "type": "string",
                            "example": ""
                          },
                          "os_type": {
                            "type": "string",
                            "example": ""
                          },
                          "monthly_price": {
                            "type": "string",
                            "example": "0"
                          }
                        }
                      }
                    },
                    "raid": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "0"
                          },
                          "short_desc": {
                            "type": "string",
                            "example": "No Raid"
                          },
                          "long_desc": {
                            "type": "string",
                            "example": "No Raid"
                          },
                          "monthly_price": {
                            "type": "string",
                            "example": "0"
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "description": "Available server configurations with pricing and hardware options."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        },
        "operationId": "buyItNowServerOrder",
        "summary": "Get configurable options for a Rapid Deploy / coupon dedicated server",
        "description": "Step 1 of the Rapid Deploy / coupon dedicated server order flow. Returns options + pricing for either a marketplace asset (`a=<asset_id>`) or a coupon (`c=<coupon_name>`) so the order form can be rendered before `placeBuyNowServer`. Read-only; no charge. Sibling ops: `placeBuyNowServer` (commit), `getMPServers` (browse marketplace), `addServer` (custom build flow).\n\n**Query (one required):**\n- `a` (integer) — asset_id from `getMPServers`.\n- `c` (string) — `server_coupons.name`.\n\n**Returns:** `{ bandwidth[], ips[], os[], cp[], raid[], regions[], a?: {asset + items}, c?: {coupon + region} }`. Each option row is `{ id, short_desc, long_desc, monthly_price }` — feed those ids into `placeBuyNowServer`.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `400` — `'No Server Coupon or Market-Place Asset Specified'` when neither `a` nor `c` is passed.\n- `400` — `'Invalid Asset ID'` / `'No Server Coupon with that name'`.\n- `409` — `'Server already sold!'` (asset already in-cart) or `'Server Out of stock'` (coupon).\n- `401` — unauthenticated.\n\n**Related calls:**\n- **Next:** `placeBuyNowServer` (commit the order).\n- **Browse:** `getMPServers`.\n- **Custom build alternative:** `addServer`.\n"
      },
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "server_id": {
                    "description": "The ID of the buy-it-now server configuration to order. Use the server listing from `GET /servers/order/buy_now_server` to find valid IDs.",
                    "type": "number",
                    "example": 2343
                  },
                  "server_hostname": {
                    "description": "The fully-qualified hostname to assign to the server.",
                    "type": "string",
                    "example": "server.int.com"
                  },
                  "server_root_password": {
                    "description": "The root or administrator password to set on the server.",
                    "type": "string",
                    "example": "uD1c!@cgD"
                  }
                }
              }
            }
          }
        },
        "tags": [
          "Servers"
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServersBuyNowResponse"
                }
              }
            },
            "description": "Order placed successfully."
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServersBuyNowError"
                }
              }
            },
            "description": "Order validation failed."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "default": {
            "description": "Default response"
          }
        },
        "operationId": "placeBuyNowServer",
        "summary": "Place a Rapid Deploy / coupon dedicated server order; creates real invoice",
        "description": "Step 2 of the Rapid Deploy / coupon order flow. Commits a marketplace asset OR coupon-based dedicated server order. Inserts the `servers` row, creates a `Repeat_Invoice` plus the first `invoices` row, marks the asset `MarketPlace-Incart` (or decrements `server_coupons.in_stock`), then emails customer + admin. **Real billable order — confirm intent first.** Sibling ops: `buyItNowServerOrder` (catalog), `getServerInfo` (poll provisioning), `getServerInvoices` (billing), `addServer` (custom build alternative).\n\n**Query (one required, same as `buyItNowServerOrder`):**\n- `a` (integer) — asset_id.\n- `c` (string) — `server_coupons.name`.\n\n**Body fields:**\n- `hostname` (string, required) — valid FQDN; validated by `valid_hostname`.\n- `enablepassword` (boolean, optional, default `false`) — when true the client must supply `rootPassword`; otherwise a secure password is generated server-side via `generate_password()`.\n- `rootPassword` (string, required when `enablepassword=true`) — must be ≥8 chars with at least one uppercase, lowercase, digit, and special character (`valid_password`).\n- `os`, `bandwidth`, `ips`, `cp`, `raid` (integer, optional) — option ids from `buyItNowServerOrder`; defaults `30` / `10` / `9` / `1` / `0` applied when missing.\n- `comments` (string, optional) — appended to the order comment.\n\n**Returns:** `201 { success: true, text: 'Server order is placed.', service_id, invoice_id }`.\n\n**Auth:** Session/API key.\n\n**Errors:**\n- `400` — `'Server Hostname is missing.'` / `'Invalid Hostname!'` / `'Server Password is missing.'` / password complexity message.\n- `409` — `'Server already sold!'` / `'Server Out of stock.'`\n- `401` — unauthenticated.\n\n**Side effects:** inserts `servers` row, creates `repeat_invoices` + `invoices` rows, updates `assets.status` or `server_coupons.in_stock`, queues admin + customer welcome emails.\n\n**Related calls:**\n- **Prerequisite:** `buyItNowServerOrder`.\n- **Next:** `getBillingInvoice` + `initiatePayment` to pay, then poll `getServerInfo` for provisioning state.\n- **Custom build alternative:** `addServer`.\n"
      }
    }
  },
  "components": {
    "schemas": {
      "AccountFeatures": {
        "title": "AccountFeatures",
        "description": "Account Features data.",
        "type": "object",
        "properties": {
          "disable_reset": {
            "format": "int32",
            "type": "integer"
          },
          "disable_reinstall": {
            "format": "int32",
            "type": "integer"
          }
        },
        "example": {
          "disable_reset": 1,
          "disable_reinstall": 1
        }
      },
      "AccountInfo": {
        "title": "AccountInfo",
        "description": "Contains the full account profile including personal info, billing details, OAuth connections, and security settings.",
        "type": "object",
        "properties": {
          "custid": {
            "type": "string"
          },
          "ima": {
            "type": "string"
          },
          "data": {
            "$ref": "#/components/schemas/AccountInfoData"
          },
          "ip": {
            "type": "string"
          },
          "oauthproviders": {
            "oneOf": [
              {
                "type": "array",
                "items": {}
              },
              {
                "$ref": "#/components/schemas/AccountInfoOauthConfigProviders"
              }
            ]
          },
          "oauthconfig": {
            "$ref": "#/components/schemas/AccountInfoOauthConfig"
          },
          "oauthadapters": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "limits": {
            "$ref": "#/components/schemas/AccountInfoLimits"
          },
          "language": {
            "type": "string"
          },
          "countryCurrencies": {
            "$ref": "#/components/schemas/AccountInfoCountryCurrencies"
          },
          "enableLocales": {
            "type": "boolean"
          },
          "enableCurrencies": {
            "type": "boolean"
          },
          "gravatar": {
            "type": "string"
          }
        },
        "example": {
          "custid": "98651",
          "ima": "client",
          "data": {
            "group": "0",
            "address": "112 Uber St.",
            "city": "Townsville",
            "country": "US",
            "disable_cc": "0",
            "fraudrecord_score": "0",
            "ima": "client",
            "name": "John Doe",
            "payment_method": "paypal",
            "phone": "8675309",
            "pin": "000000",
            "state": "PA",
            "status": "active",
            "zip": "11111",
            "account_id": "98651",
            "account_lid": "user@domain.com",
            "address2": "",
            "affiliate_dock_description": "Use this coupon when placing an order to get the first month of hosting for only 1 penny.",
            "affiliate_dock_title": "Exclusive offer to viewers",
            "affiliate_payment_method": "paypal",
            "affiliate_paypal": "user@domain.com",
            "cc": "************1111",
            "cc_auto": "0",
            "cc_exp": "05/2019",
            "cc_type": "visa",
            "cc_whitelist": "1",
            "ccs": {
              "1": {
                "cc": "************1111",
                "cc_exp": "05/2019",
                "name": "John Doe",
                "country": "US",
                "verified": true
              },
              "5": {
                "cc": "************2222",
                "cc_exp": "05/2019",
                "name": "John Doe",
                "address": "123 Uber St",
                "city": "Townsville",
                "state": "PA",
                "zip": "11122",
                "country": "US",
                "maxmind_riskscore": "0.25",
                "maxmind": {
                  "distance": "0",
                  "countryMatch": "Yes",
                  "countryCode": "US",
                  "freeMail": "No",
                  "anonymousProxy": "No",
                  "binMatch": "No",
                  "binCountry": "IN",
                  "err": "",
                  "proxyScore": "0.00",
                  "ip_region": "PA",
                  "ip_city": "Townsville",
                  "ip_latitude": "40.175",
                  "ip_longitude": "-76.1817",
                  "binName": "state bank of india",
                  "ip_isp": "PenTeleData",
                  "ip_org": "PenTeleData",
                  "binNameMatch": "NA",
                  "binPhoneMatch": "NA",
                  "binPhone": "",
                  "custPhoneInBillingLoc": "NotFound",
                  "highRiskCountry": "No",
                  "queriesRemaining": "2764",
                  "cityPostalMatch": "Yes",
                  "shipCityPostalMatch": "",
                  "maxmindID": "7IVXKOVV",
                  "ip_asnum": "AS3737 AS-PTD",
                  "ip_userType": "residential",
                  "ip_countryConf": "99",
                  "ip_regionConf": "99",
                  "ip_cityConf": "30",
                  "ip_postalCode": "11122",
                  "ip_postalConf": "30",
                  "ip_accuracyRadius": "20",
                  "ip_netSpeedCell": "Cable/DSL",
                  "ip_metroCode": "566",
                  "ip_areaCode": "",
                  "ip_timeZone": "America/New_York",
                  "ip_regionName": "Pennsylvania",
                  "ip_domain": "ptd.net",
                  "ip_countryName": "United States",
                  "ip_continentCode": "NA",
                  "ip_corporateProxy": "No",
                  "isTransProxy": "No",
                  "carderEmail": "No",
                  "highRiskUsername": "No",
                  "highRiskPassword": "No",
                  "riskScore": 0.25,
                  "prepaid": "No",
                  "minfraud_version": "1.3",
                  "service_level": "premium",
                  "female_name": "no"
                },
                "verified": true
              },
              "6": {
                "cc": "************1111",
                "cc_exp": "05/2021",
                "name": "John Doe",
                "city": "Townsville",
                "state": "PA",
                "zip": "11122",
                "country": "US",
                "maxmind_riskscore": "0.48",
                "maxmind": {
                  "distance": "0",
                  "countryMatch": "Yes",
                  "countryCode": "US",
                  "freeMail": "No",
                  "anonymousProxy": "No",
                  "binMatch": "Yes",
                  "binCountry": "US",
                  "err": "",
                  "proxyScore": "0.00",
                  "ip_region": "PA",
                  "ip_city": "Townsville",
                  "ip_latitude": "40.175",
                  "ip_longitude": "-76.1817",
                  "binName": "Bank of No Hope",
                  "ip_isp": "PenTeleData",
                  "ip_org": "PenTeleData",
                  "binNameMatch": "NA",
                  "binPhoneMatch": "NA",
                  "binPhone": "1234561234",
                  "custPhoneInBillingLoc": "NotFound",
                  "highRiskCountry": "No",
                  "queriesRemaining": "2765",
                  "cityPostalMatch": "Yes",
                  "shipCityPostalMatch": "",
                  "maxmindID": "AAAAAZZZZZ",
                  "ip_asnum": "AS3737 AS-PTD",
                  "ip_userType": "residential",
                  "ip_countryConf": "99",
                  "ip_regionConf": "99",
                  "ip_cityConf": "30",
                  "ip_postalCode": "11122",
                  "ip_postalConf": "30",
                  "ip_accuracyRadius": "20",
                  "ip_netSpeedCell": "Cable/DSL",
                  "ip_metroCode": "566",
                  "ip_areaCode": "",
                  "ip_timeZone": "America/New_York",
                  "ip_regionName": "Pennsylvania",
                  "ip_domain": "ptd.net",
                  "ip_countryName": "United States",
                  "ip_continentCode": "NA",
                  "ip_corporateProxy": "No",
                  "isTransProxy": "No",
                  "carderEmail": "No",
                  "highRiskUsername": "No",
                  "highRiskPassword": "No",
                  "riskScore": 0.48,
                  "prepaid": "No",
                  "minfraud_version": "1.3",
                  "service_level": "premium",
                  "female_name": "no"
                },
                "verified": true
              }
            },
            "ccs_added": "14",
            "company": "InterServer, Inc.",
            "currency": "USD",
            "disable_reinstall": "0",
            "disable_reset": "0",
            "email": "user@domain.com",
            "email_abuse": "",
            "email_settings": {
              "admin/cc_bad_response": "1",
              "admin/mass_communications.tpl": "1"
            },
            "extra": {
              "private_whois": "0"
            },
            "facebook_id": "111111111111111",
            "facebook_url": "https://www.facebook.com/111111111111111",
            "firstname": "John",
            "fraudrecord": {
              "score": "0",
              "count": "0",
              "reliability": "0.0",
              "code": "zxcvzxcvzxcv"
            },
            "github_id": "2222222",
            "github_url": "https://github.com/user",
            "google_id": "355431342512341234",
            "google_url": "https://plus.google.com/+JohnDoe",
            "innertell_id": "2222222",
            "lastname": "Doe",
            "locale": "auto",
            "maxmind": {
              "distance": "6146",
              "countryMatch": "No",
              "countryCode": "US",
              "freeMail": "No",
              "anonymousProxy": "No",
              "score": "5.00",
              "binMatch": "NotFound",
              "binCountry": "",
              "err": "CITY_NOT_FOUND",
              "proxyScore": "0.00",
              "ip_region": "PA",
              "ip_city": "Townsville",
              "ip_latitude": "40.1767",
              "ip_longitude": "-76.4297",
              "binName": "",
              "ip_isp": "PenTeleData",
              "ip_org": "PenTeleData",
              "binNameMatch": "NA",
              "binPhoneMatch": "NA",
              "binPhone": "",
              "custPhoneInBillingLoc": "",
              "highRiskCountry": "No",
              "queriesRemaining": "171",
              "cityPostalMatch": "",
              "shipCityPostalMatch": "",
              "maxmindID": "HMOCUJP7",
              "ip_asnum": "AS3737 PenTeleData Inc.",
              "ip_userType": "residential",
              "ip_countryConf": "99",
              "ip_regionConf": "97",
              "ip_cityConf": "30",
              "ip_postalCode": "11122",
              "ip_postalConf": "30",
              "ip_accuracyRadius": "10",
              "ip_netSpeedCell": "Dialup",
              "ip_metroCode": "566",
              "ip_areaCode": "333",
              "ip_timeZone": "America/New_York",
              "ip_regionName": "Pennsylvania",
              "ip_domain": "ptd.net",
              "ip_countryName": "United States",
              "ip_continentCode": "NA",
              "ip_corporateProxy": "No",
              "carderEmail": "No",
              "highRiskUsername": "No",
              "riskScore": "4.82",
              "explanation": "You should review this order carefully, as it is considered high risk. We suggest you be very cautious about accepting this order. This order is higher risk because the distance between the billing address and the user's actual location is so great. The order is higher risk because the billing country and the country in which the IP address is located don't match",
              "female_name": "no"
            },
            "maxmind_score": "0",
            "mb_id": "4769",
            "modernbill_id": "1234",
            "picture": "https://avatars3.githubusercontent.com/u/1364504",
            "referrer_coupon": "detainaffiliatecoupon",
            "reseller_markup": "10",
            "username": "user@domain.com",
            "ssh_key": "zzzz",
            "ssh_key_wrapped": "zzzz",
            "api_key": "aaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbccccccccccccccccccc",
            "api_key_wrapped": "aaaaaaaaaaaaaaaaaaaaaaa<br>bbbbbbbbbbbbb<br>ccccccccccccccccccc",
            "2fa_google_key": "ssssssssssssssssssssssssss",
            "2fa_google_enabled": true,
            "2fa_google": 1,
            "2fa_google_split": "zzzz zzzz zzzz zzzz ",
            "2fa_google_qr": "data:image/png;base64,zzz"
          },
          "ip": "1.2.3.4",
          "oauthproviders": [],
          "oauthconfig": {
            "callback": "https://my.interserver.net/oauth/callback.php",
            "providers": {
              "Twitter": {
                "enabled": false,
                "linked": false
              },
              "Facebook": {
                "enabled": true,
                "account": "111111111111111111111",
                "url": "https://www.facebook.com/111111111111111111111",
                "linked": true
              },
              "Google": {
                "enabled": true,
                "account": "111111111111111111111",
                "url": "https://plus.google.com/+UserName",
                "linked": true
              },
              "GitHub": {
                "enabled": true,
                "account": "111111111111111111111",
                "url": "https://github.com/detain",
                "linked": true
              }
            }
          },
          "oauthadapters": [],
          "limits": [
            {
              "start": "1.1.1.1",
              "end": "1.1.1.254"
            },
            {
              "start": "2600:387:0:809::1b",
              "end": "2600:387:0:809::1b"
            }
          ],
          "language": "en-US",
          "countryCurrencies": {
            "CN": [
              "USD"
            ],
            "BR": [
              "USD",
              "BRL"
            ],
            "RU": [
              "USD"
            ]
          },
          "enableLocales": true,
          "enableCurrencies": false,
          "gravatar": "https://avatars3.githubusercontent.com/u/1364504"
        }
      },
      "AccountInfoCountryCurrencies": {
        "title": "AccountInfoCountryCurrencies",
        "description": "Maps country codes to their available currency options.",
        "type": "object",
        "additionalProperties": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "example": {
          "CN": [
            "USD"
          ],
          "BR": [
            "USD",
            "BRL"
          ],
          "RU": [
            "USD"
          ]
        }
      },
      "AccountInfoData": {
        "title": "AccountInfoData",
        "description": "Detailed account profile data including contact info, billing, API keys, and security settings.",
        "type": "object",
        "properties": {
          "group": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "disable_cc": {
            "type": "string"
          },
          "fraudrecord_score": {
            "type": "string"
          },
          "ima": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "payment_method": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "pin": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "account_id": {
            "type": "string"
          },
          "account_lid": {
            "type": "string"
          },
          "address2": {
            "type": "string"
          },
          "affiliate_dock_description": {
            "type": "string"
          },
          "affiliate_dock_title": {
            "type": "string"
          },
          "affiliate_payment_method": {
            "type": "string"
          },
          "affiliate_paypal": {
            "type": "string"
          },
          "cc": {
            "type": "string"
          },
          "cc_auto": {
            "type": "string"
          },
          "cc_exp": {
            "type": "string"
          },
          "cc_type": {
            "type": "string"
          },
          "cc_whitelist": {
            "type": "string"
          },
          "ccs": {
            "$ref": "#/components/schemas/AccountInfoDataCcs"
          },
          "ccs_added": {
            "type": "string"
          },
          "company": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "disable_reinstall": {
            "type": "string"
          },
          "disable_reset": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "email_abuse": {
            "type": "string"
          },
          "email_settings": {
            "type": "object",
            "properties": {
              "admin/cc_bad_response": {
                "type": "string"
              },
              "admin/mass_communications.tpl": {
                "type": "string"
              }
            }
          },
          "extra": {
            "type": "object",
            "properties": {
              "private_whois": {
                "type": "string"
              }
            }
          },
          "facebook_id": {
            "type": "string"
          },
          "facebook_url": {
            "type": "string"
          },
          "firstname": {
            "type": "string"
          },
          "fraudrecord": {
            "type": "object",
            "properties": {
              "score": {
                "type": "string"
              },
              "count": {
                "type": "string"
              },
              "reliability": {
                "type": "string"
              },
              "code": {
                "type": "string"
              }
            }
          },
          "github_id": {
            "type": "string"
          },
          "github_url": {
            "type": "string"
          },
          "google_id": {
            "type": "string"
          },
          "google_url": {
            "type": "string"
          },
          "innertell_id": {
            "type": "string"
          },
          "lastname": {
            "type": "string"
          },
          "locale": {
            "type": "string"
          },
          "maxmind": {
            "$ref": "#/components/schemas/AccountInfoMaxMindResponse"
          },
          "maxmind_score": {
            "type": "string"
          },
          "mb_id": {
            "type": "string"
          },
          "modernbill_id": {
            "type": "string"
          },
          "picture": {
            "type": "string"
          },
          "referrer_coupon": {
            "type": "string"
          },
          "reseller_markup": {
            "type": "string"
          },
          "username": {
            "type": "string"
          },
          "ssh_key": {
            "type": "string"
          },
          "ssh_key_wrapped": {
            "type": "string"
          },
          "api_key": {
            "type": "string"
          },
          "api_key_wrapped": {
            "type": "string"
          },
          "2fa_google_key": {
            "type": "string"
          },
          "2fa_google_enabled": {
            "type": "boolean"
          },
          "2fa_google": {
            "format": "int32",
            "type": "integer"
          },
          "2fa_google_split": {
            "type": "string"
          },
          "2fa_google_qr": {
            "type": "string"
          }
        },
        "example": {
          "group": "0",
          "address": "112 Uber St.",
          "city": "Townsville",
          "country": "US",
          "disable_cc": "0",
          "fraudrecord_score": "0",
          "ima": "client",
          "name": "John Doe",
          "payment_method": "paypal",
          "phone": "8675309",
          "pin": "000000",
          "state": "PA",
          "status": "active",
          "zip": "11111",
          "account_id": "98651",
          "account_lid": "user@domain.com",
          "address2": "",
          "affiliate_dock_description": "Use this coupon when placing an order to get the first month of hosting for only 1 penny.",
          "affiliate_dock_title": "Exclusive offer to viewers",
          "affiliate_payment_method": "paypal",
          "affiliate_paypal": "user@domain.com",
          "cc": "************1111",
          "cc_auto": "0",
          "cc_exp": "05/2019",
          "cc_type": "visa",
          "cc_whitelist": "1",
          "ccs": {
            "1": {
              "cc": "************1111",
              "cc_exp": "05/2019",
              "name": "John Doe",
              "country": "US",
              "verified": true
            },
            "5": {
              "cc": "************2222",
              "cc_exp": "05/2019",
              "name": "John Doe",
              "address": "123 Uber St",
              "city": "Townsville",
              "state": "PA",
              "zip": "11122",
              "country": "US",
              "maxmind_riskscore": "0.25",
              "maxmind": {
                "distance": "0",
                "countryMatch": "Yes",
                "countryCode": "US",
                "freeMail": "No",
                "anonymousProxy": "No",
                "binMatch": "No",
                "binCountry": "IN",
                "err": "",
                "proxyScore": "0.00",
                "ip_region": "PA",
                "ip_city": "Townsville",
                "ip_latitude": "40.175",
                "ip_longitude": "-76.1817",
                "binName": "state bank of india",
                "ip_isp": "PenTeleData",
                "ip_org": "PenTeleData",
                "binNameMatch": "NA",
                "binPhoneMatch": "NA",
                "binPhone": "",
                "custPhoneInBillingLoc": "NotFound",
                "highRiskCountry": "No",
                "queriesRemaining": "2764",
                "cityPostalMatch": "Yes",
                "shipCityPostalMatch": "",
                "maxmindID": "7IVXKOVV",
                "ip_asnum": "AS3737 AS-PTD",
                "ip_userType": "residential",
                "ip_countryConf": "99",
                "ip_regionConf": "99",
                "ip_cityConf": "30",
                "ip_postalCode": "11122",
                "ip_postalConf": "30",
                "ip_accuracyRadius": "20",
                "ip_netSpeedCell": "Cable/DSL",
                "ip_metroCode": "566",
                "ip_areaCode": "",
                "ip_timeZone": "America/New_York",
                "ip_regionName": "Pennsylvania",
                "ip_domain": "ptd.net",
                "ip_countryName": "United States",
                "ip_continentCode": "NA",
                "ip_corporateProxy": "No",
                "isTransProxy": "No",
                "carderEmail": "No",
                "highRiskUsername": "No",
                "highRiskPassword": "No",
                "riskScore": 0.25,
                "prepaid": "No",
                "minfraud_version": "1.3",
                "service_level": "premium",
                "female_name": "no"
              },
              "verified": true
            },
            "6": {
              "cc": "************1111",
              "cc_exp": "05/2021",
              "name": "John Doe",
              "city": "Townsville",
              "state": "PA",
              "zip": "11122",
              "country": "US",
              "maxmind_riskscore": "0.48",
              "maxmind": {
                "distance": "0",
                "countryMatch": "Yes",
                "countryCode": "US",
                "freeMail": "No",
                "anonymousProxy": "No",
                "binMatch": "Yes",
                "binCountry": "US",
                "err": "",
                "proxyScore": "0.00",
                "ip_region": "PA",
                "ip_city": "Townsville",
                "ip_latitude": "40.175",
                "ip_longitude": "-76.1817",
                "binName": "Bank of No Hope",
                "ip_isp": "PenTeleData",
                "ip_org": "PenTeleData",
                "binNameMatch": "NA",
                "binPhoneMatch": "NA",
                "binPhone": "1234561234",
                "custPhoneInBillingLoc": "NotFound",
                "highRiskCountry": "No",
                "queriesRemaining": "2765",
                "cityPostalMatch": "Yes",
                "shipCityPostalMatch": "",
                "maxmindID": "AAAAAZZZZZ",
                "ip_asnum": "AS3737 AS-PTD",
                "ip_userType": "residential",
                "ip_countryConf": "99",
                "ip_regionConf": "99",
                "ip_cityConf": "30",
                "ip_postalCode": "11122",
                "ip_postalConf": "30",
                "ip_accuracyRadius": "20",
                "ip_netSpeedCell": "Cable/DSL",
                "ip_metroCode": "566",
                "ip_areaCode": "",
                "ip_timeZone": "America/New_York",
                "ip_regionName": "Pennsylvania",
                "ip_domain": "ptd.net",
                "ip_countryName": "United States",
                "ip_continentCode": "NA",
                "ip_corporateProxy": "No",
                "isTransProxy": "No",
                "carderEmail": "No",
                "highRiskUsername": "No",
                "highRiskPassword": "No",
                "riskScore": 0.48,
                "prepaid": "No",
                "minfraud_version": "1.3",
                "service_level": "premium",
                "female_name": "no"
              },
              "verified": true
            }
          },
          "ccs_added": "14",
          "company": "InterServer, Inc.",
          "currency": "USD",
          "disable_reinstall": "0",
          "disable_reset": "0",
          "email": "user@domain.com",
          "email_abuse": "",
          "email_settings": {
            "admin/cc_bad_response": "1",
            "admin/mass_communications.tpl": "1"
          },
          "extra": {
            "private_whois": "0"
          },
          "facebook_id": "111111111111111",
          "facebook_url": "https://www.facebook.com/111111111111111",
          "firstname": "John",
          "fraudrecord": {
            "score": "0",
            "count": "0",
            "reliability": "0.0",
            "code": "zxcvzxcvzxcv"
          },
          "github_id": "2222222",
          "github_url": "https://github.com/user",
          "google_id": "355431342512341234",
          "google_url": "https://plus.google.com/+JohnDoe",
          "innertell_id": "2222222",
          "lastname": "Doe",
          "locale": "auto",
          "maxmind": {
            "distance": "6146",
            "countryMatch": "No",
            "countryCode": "US",
            "freeMail": "No",
            "anonymousProxy": "No",
            "score": "5.00",
            "binMatch": "NotFound",
            "binCountry": "",
            "err": "CITY_NOT_FOUND",
            "proxyScore": "0.00",
            "ip_region": "PA",
            "ip_city": "Townsville",
            "ip_latitude": "40.1767",
            "ip_longitude": "-76.4297",
            "binName": "",
            "ip_isp": "PenTeleData",
            "ip_org": "PenTeleData",
            "binNameMatch": "NA",
            "binPhoneMatch": "NA",
            "binPhone": "",
            "custPhoneInBillingLoc": "",
            "highRiskCountry": "No",
            "queriesRemaining": "171",
            "cityPostalMatch": "",
            "shipCityPostalMatch": "",
            "maxmindID": "HMOCUJP7",
            "ip_asnum": "AS3737 PenTeleData Inc.",
            "ip_userType": "residential",
            "ip_countryConf": "99",
            "ip_regionConf": "97",
            "ip_cityConf": "30",
            "ip_postalCode": "11122",
            "ip_postalConf": "30",
            "ip_accuracyRadius": "10",
            "ip_netSpeedCell": "Dialup",
            "ip_metroCode": "566",
            "ip_areaCode": "333",
            "ip_timeZone": "America/New_York",
            "ip_regionName": "Pennsylvania",
            "ip_domain": "ptd.net",
            "ip_countryName": "United States",
            "ip_continentCode": "NA",
            "ip_corporateProxy": "No",
            "carderEmail": "No",
            "highRiskUsername": "No",
            "riskScore": "4.82",
            "explanation": "You should review this order carefully, as it is considered high risk. We suggest you be very cautious about accepting this order. This order is higher risk because the distance between the billing address and the user's actual location is so great. The order is higher risk because the billing country and the country in which the IP address is located don't match",
            "female_name": "no"
          },
          "maxmind_score": "0",
          "mb_id": "4769",
          "modernbill_id": "1234",
          "picture": "https://avatars3.githubusercontent.com/u/1364504",
          "referrer_coupon": "detainaffiliatecoupon",
          "reseller_markup": "10",
          "username": "user@domain.com",
          "ssh_key": "zzzz",
          "ssh_key_wrapped": "zzzz",
          "api_key": "aaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbccccccccccccccccccc",
          "api_key_wrapped": "aaaaaaaaaaaaaaaaaaaaaaa<br>bbbbbbbbbbbbb<br>ccccccccccccccccccc",
          "2fa_google_key": "ssssssssssssssssssssssssss",
          "2fa_google_enabled": true,
          "2fa_google": 1,
          "2fa_google_split": "zzzz zzzz zzzz zzzz ",
          "2fa_google_qr": "data:image/png;base64,zzz"
        }
      },
      "AccountInfoDataCc": {
        "title": "AccountInfoDataCc",
        "description": "Credit card information stored on the account.",
        "type": "object",
        "properties": {
          "cc": {
            "type": "string"
          },
          "cc_exp": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "maxmind_riskscore": {
            "type": "string"
          },
          "maxmind": {
            "$ref": "#/components/schemas/AccountInfoMaxMindResponse"
          },
          "verified": {
            "type": "boolean"
          }
        },
        "example": {
          "cc": "************2222",
          "cc_exp": "05/2019",
          "name": "John Doe",
          "address": "123 Uber St",
          "city": "Townsville",
          "state": "PA",
          "zip": "11122",
          "country": "US",
          "maxmind_riskscore": "0.25",
          "maxmind": {
            "distance": "0",
            "countryMatch": "Yes",
            "countryCode": "US",
            "freeMail": "No",
            "anonymousProxy": "No",
            "binMatch": "No",
            "binCountry": "IN",
            "err": "",
            "proxyScore": "0.00",
            "ip_region": "PA",
            "ip_city": "Townsville",
            "ip_latitude": "40.175",
            "ip_longitude": "-76.1817",
            "binName": "state bank of india",
            "ip_isp": "PenTeleData",
            "ip_org": "PenTeleData",
            "binNameMatch": "NA",
            "binPhoneMatch": "NA",
            "binPhone": "",
            "custPhoneInBillingLoc": "NotFound",
            "highRiskCountry": "No",
            "queriesRemaining": "2764",
            "cityPostalMatch": "Yes",
            "shipCityPostalMatch": "",
            "maxmindID": "7IVXKOVV",
            "ip_asnum": "AS3737 AS-PTD",
            "ip_userType": "residential",
            "ip_countryConf": "99",
            "ip_regionConf": "99",
            "ip_cityConf": "30",
            "ip_postalCode": "11122",
            "ip_postalConf": "30",
            "ip_accuracyRadius": "20",
            "ip_netSpeedCell": "Cable/DSL",
            "ip_metroCode": "566",
            "ip_areaCode": "",
            "ip_timeZone": "America/New_York",
            "ip_regionName": "Pennsylvania",
            "ip_domain": "ptd.net",
            "ip_countryName": "United States",
            "ip_continentCode": "NA",
            "ip_corporateProxy": "No",
            "isTransProxy": "No",
            "carderEmail": "No",
            "highRiskUsername": "No",
            "highRiskPassword": "No",
            "riskScore": 0.25,
            "prepaid": "No",
            "minfraud_version": "1.3",
            "service_level": "premium",
            "female_name": "no"
          },
          "verified": true
        }
      },
      "AccountInfoDataCcs": {
        "title": "AccountInfoDataCcs",
        "description": "Collection of credit cards on the account, keyed by card ID.",
        "type": "object",
        "additionalProperties": {
          "$ref": "#/components/schemas/AccountInfoDataCc"
        },
        "example": {
          "1": {
            "cc": "************1111",
            "cc_exp": "05/2019",
            "name": "John Doe",
            "country": "US",
            "verified": true
          },
          "5": {
            "cc": "************2222",
            "cc_exp": "05/2019",
            "name": "John Doe",
            "address": "123 Uber St",
            "city": "Townsville",
            "state": "PA",
            "zip": "11122",
            "country": "US",
            "maxmind_riskscore": "0.25",
            "maxmind": {
              "distance": "0",
              "countryMatch": "Yes",
              "countryCode": "US",
              "freeMail": "No",
              "anonymousProxy": "No",
              "binMatch": "No",
              "binCountry": "IN",
              "err": "",
              "proxyScore": "0.00",
              "ip_region": "PA",
              "ip_city": "Townsville",
              "ip_latitude": "40.175",
              "ip_longitude": "-76.1817",
              "binName": "state bank of india",
              "ip_isp": "PenTeleData",
              "ip_org": "PenTeleData",
              "binNameMatch": "NA",
              "binPhoneMatch": "NA",
              "binPhone": "",
              "custPhoneInBillingLoc": "NotFound",
              "highRiskCountry": "No",
              "queriesRemaining": "2764",
              "cityPostalMatch": "Yes",
              "shipCityPostalMatch": "",
              "maxmindID": "7IVXKOVV",
              "ip_asnum": "AS3737 AS-PTD",
              "ip_userType": "residential",
              "ip_countryConf": "99",
              "ip_regionConf": "99",
              "ip_cityConf": "30",
              "ip_postalCode": "11122",
              "ip_postalConf": "30",
              "ip_accuracyRadius": "20",
              "ip_netSpeedCell": "Cable/DSL",
              "ip_metroCode": "566",
              "ip_areaCode": "",
              "ip_timeZone": "America/New_York",
              "ip_regionName": "Pennsylvania",
              "ip_domain": "ptd.net",
              "ip_countryName": "United States",
              "ip_continentCode": "NA",
              "ip_corporateProxy": "No",
              "isTransProxy": "No",
              "carderEmail": "No",
              "highRiskUsername": "No",
              "highRiskPassword": "No",
              "riskScore": 0.25,
              "prepaid": "No",
              "minfraud_version": "1.3",
              "service_level": "premium",
              "female_name": "no"
            },
            "verified": true
          },
          "6": {
            "cc": "************1111",
            "cc_exp": "05/2021",
            "name": "John Doe",
            "city": "Townsville",
            "state": "PA",
            "zip": "11122",
            "country": "US",
            "maxmind_riskscore": "0.48",
            "maxmind": {
              "distance": "0",
              "countryMatch": "Yes",
              "countryCode": "US",
              "freeMail": "No",
              "anonymousProxy": "No",
              "binMatch": "Yes",
              "binCountry": "US",
              "err": "",
              "proxyScore": "0.00",
              "ip_region": "PA",
              "ip_city": "Townsville",
              "ip_latitude": "40.175",
              "ip_longitude": "-76.1817",
              "binName": "Bank of No Hope",
              "ip_isp": "PenTeleData",
              "ip_org": "PenTeleData",
              "binNameMatch": "NA",
              "binPhoneMatch": "NA",
              "binPhone": "1234561234",
              "custPhoneInBillingLoc": "NotFound",
              "highRiskCountry": "No",
              "queriesRemaining": "2765",
              "cityPostalMatch": "Yes",
              "shipCityPostalMatch": "",
              "maxmindID": "AAAAAZZZZZ",
              "ip_asnum": "AS3737 AS-PTD",
              "ip_userType": "residential",
              "ip_countryConf": "99",
              "ip_regionConf": "99",
              "ip_cityConf": "30",
              "ip_postalCode": "11122",
              "ip_postalConf": "30",
              "ip_accuracyRadius": "20",
              "ip_netSpeedCell": "Cable/DSL",
              "ip_metroCode": "566",
              "ip_areaCode": "",
              "ip_timeZone": "America/New_York",
              "ip_regionName": "Pennsylvania",
              "ip_domain": "ptd.net",
              "ip_countryName": "United States",
              "ip_continentCode": "NA",
              "ip_corporateProxy": "No",
              "isTransProxy": "No",
              "carderEmail": "No",
              "highRiskUsername": "No",
              "highRiskPassword": "No",
              "riskScore": 0.48,
              "prepaid": "No",
              "minfraud_version": "1.3",
              "service_level": "premium",
              "female_name": "no"
            },
            "verified": true
          }
        }
      },
      "AccountInfoLimits": {
        "title": "AccountInfoLimits",
        "description": "IP address ranges used to restrict account access to specific IPs.",
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "start": {
              "type": "string"
            },
            "end": {
              "type": "string"
            }
          }
        },
        "example": [
          {
            "start": "8.7.6.5",
            "end": "8.7.6.5"
          },
          {
            "start": "107.77.194.166",
            "end": "107.77.194.166"
          },
          {
            "start": "2600:387:0:809::1b",
            "end": "2600:387:0:809::1b"
          },
          {
            "start": "192.64.80.218",
            "end": "192.64.80.218"
          },
          {
            "start": "66.45.235.102",
            "end": "66.45.235.102"
          }
        ]
      },
      "AccountInfoMaxMindResponse": {
        "title": "AccountInfoMaxMindResponse",
        "description": "MaxMind fraud detection scoring data for a credit card transaction.",
        "type": "object",
        "properties": {
          "distance": {
            "type": "string"
          },
          "countryMatch": {
            "type": "string"
          },
          "countryCode": {
            "type": "string"
          },
          "freeMail": {
            "type": "string"
          },
          "anonymousProxy": {
            "type": "string"
          },
          "score": {
            "type": "string"
          },
          "binMatch": {
            "type": "string"
          },
          "binCountry": {
            "type": "string"
          },
          "err": {
            "type": "string"
          },
          "proxyScore": {
            "type": "string"
          },
          "ip_region": {
            "type": "string"
          },
          "ip_city": {
            "type": "string"
          },
          "ip_latitude": {
            "type": "string"
          },
          "ip_longitude": {
            "type": "string"
          },
          "binName": {
            "type": "string"
          },
          "ip_isp": {
            "type": "string"
          },
          "ip_org": {
            "type": "string"
          },
          "binNameMatch": {
            "type": "string"
          },
          "binPhoneMatch": {
            "type": "string"
          },
          "binPhone": {
            "type": "string"
          },
          "custPhoneInBillingLoc": {
            "type": "string"
          },
          "highRiskCountry": {
            "type": "string"
          },
          "queriesRemaining": {
            "type": "string"
          },
          "cityPostalMatch": {
            "type": "string"
          },
          "shipCityPostalMatch": {
            "type": "string"
          },
          "maxmindID": {
            "type": "string"
          },
          "ip_asnum": {
            "type": "string"
          },
          "ip_userType": {
            "type": "string"
          },
          "ip_countryConf": {
            "type": "string"
          },
          "ip_regionConf": {
            "type": "string"
          },
          "ip_cityConf": {
            "type": "string"
          },
          "ip_postalCode": {
            "type": "string"
          },
          "ip_postalConf": {
            "type": "string"
          },
          "ip_accuracyRadius": {
            "type": "string"
          },
          "ip_netSpeedCell": {
            "type": "string"
          },
          "ip_metroCode": {
            "type": "string"
          },
          "ip_areaCode": {
            "type": "string"
          },
          "ip_timeZone": {
            "type": "string"
          },
          "ip_regionName": {
            "type": "string"
          },
          "ip_domain": {
            "type": "string"
          },
          "ip_countryName": {
            "type": "string"
          },
          "ip_continentCode": {
            "type": "string"
          },
          "ip_corporateProxy": {
            "type": "string"
          },
          "carderEmail": {
            "type": "string"
          },
          "highRiskUsername": {
            "type": "string"
          },
          "highRiskPassword": {
            "type": "string"
          },
          "riskScore": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ]
          },
          "isTransProxy": {
            "type": "string"
          },
          "prepaid": {
            "type": "string"
          },
          "minfraud_version": {
            "type": "string"
          },
          "service_level": {
            "type": "string"
          },
          "explanation": {
            "type": "string"
          },
          "female_name": {
            "type": "string"
          }
        },
        "example": {
          "distance": "6146",
          "countryMatch": "No",
          "countryCode": "US",
          "freeMail": "No",
          "anonymousProxy": "No",
          "score": "5.00",
          "binMatch": "NotFound",
          "binCountry": "",
          "err": "CITY_NOT_FOUND",
          "proxyScore": "0.00",
          "ip_region": "PA",
          "ip_city": "Townsville",
          "ip_latitude": "40.1767",
          "ip_longitude": "-76.4297",
          "binName": "",
          "ip_isp": "PenTeleData",
          "ip_org": "PenTeleData",
          "binNameMatch": "NA",
          "binPhoneMatch": "NA",
          "binPhone": "",
          "custPhoneInBillingLoc": "",
          "highRiskCountry": "No",
          "queriesRemaining": "171",
          "cityPostalMatch": "",
          "shipCityPostalMatch": "",
          "maxmindID": "HMOCUJP7",
          "ip_asnum": "AS3737 PenTeleData Inc.",
          "ip_userType": "residential",
          "ip_countryConf": "99",
          "ip_regionConf": "97",
          "ip_cityConf": "30",
          "ip_postalCode": "11122",
          "ip_postalConf": "30",
          "ip_accuracyRadius": "10",
          "ip_netSpeedCell": "Dialup",
          "ip_metroCode": "566",
          "ip_areaCode": "333",
          "ip_timeZone": "America/New_York",
          "ip_regionName": "Pennsylvania",
          "ip_domain": "ptd.net",
          "ip_countryName": "United States",
          "ip_continentCode": "NA",
          "ip_corporateProxy": "No",
          "carderEmail": "No",
          "highRiskUsername": "No",
          "riskScore": "4.82",
          "explanation": "You should review this order carefully, as it is considered high risk. We suggest you be very cautious about accepting this order. This order is higher risk because the distance between the billing address and the user's actual location is so great. The order is higher risk because the billing country and the country in which the IP address is located don't match",
          "female_name": "no"
        }
      },
      "AccountInfoOauthConfig": {
        "title": "AccountInfoOauthConfig",
        "description": "OAuth integration configuration including callback URL and available providers.",
        "required": [
          "providers",
          "callback"
        ],
        "type": "object",
        "properties": {
          "callback": {
            "type": "string"
          },
          "providers": {
            "$ref": "#/components/schemas/AccountInfoOauthConfigProviders"
          }
        },
        "example": {
          "callback": "https://my.interserver.net/oauth/callback.php",
          "providers": {
            "Twitter": {
              "enabled": false,
              "linked": false
            },
            "Facebook": {
              "enabled": true,
              "account": "10204015683980049",
              "url": "https://www.facebook.com/10204015683980049",
              "linked": true
            },
            "Google": {
              "enabled": true,
              "account": "103826860529802474211",
              "url": "https://plus.google.com/+JoeHuss",
              "linked": true
            },
            "GitHub": {
              "enabled": true,
              "account": "1364504",
              "url": "https://github.com/detain",
              "linked": true
            }
          }
        }
      },
      "AccountInfoOauthConfigProviders": {
        "title": "AccountInfoOauthConfigProviders",
        "description": "Map of OAuth providers and their linked status on the account.",
        "type": "object",
        "additionalProperties": {
          "type": "object",
          "properties": {
            "enabled": {
              "type": "boolean"
            },
            "linked": {
              "type": "boolean"
            },
            "account": {
              "type": "string"
            },
            "url": {
              "type": "string"
            }
          }
        },
        "example": {
          "Twitter": {
            "enabled": false,
            "linked": false
          },
          "Facebook": {
            "enabled": true,
            "account": "10204015683980049",
            "url": "https://www.facebook.com/10204015683980049",
            "linked": true
          },
          "Google": {
            "enabled": true,
            "account": "103826860529802474211",
            "url": "https://plus.google.com/+JoeHuss",
            "linked": true
          },
          "GitHub": {
            "enabled": true,
            "account": "1364504",
            "url": "https://github.com/detain",
            "linked": true
          }
        }
      },
      "AccountInfoPost": {
        "title": "Account Information Update Request",
        "description": "Request to update account information.",
        "required": [
          "name",
          "address",
          "city",
          "country",
          "phone",
          "state",
          "zip"
        ],
        "type": "object",
        "properties": {
          "name": {
            "description": "Your name.",
            "type": "string",
            "example": "John Doe"
          },
          "company": {
            "description": "Your company name.",
            "type": "string",
            "example": "My Company"
          },
          "address": {
            "description": "Your address.",
            "type": "string",
            "example": "124 My St"
          },
          "address2": {
            "description": "Additional address information.",
            "type": "string"
          },
          "city": {
            "description": "Your city.",
            "type": "string",
            "example": "My Town"
          },
          "state": {
            "description": "Your state.",
            "type": "string",
            "example": "PA"
          },
          "zip": {
            "description": "Your ZIP code.",
            "type": "string",
            "example": "17522"
          },
          "country": {
            "description": "Your country.",
            "type": "string",
            "example": "US"
          },
          "phone": {
            "description": "Your phone number.",
            "type": "string",
            "example": "8675309"
          },
          "locale": {
            "description": "Your preferred locale.",
            "type": "string"
          },
          "email_invoices": {
            "description": "Your email for invoice notifications.",
            "type": "string"
          },
          "email_abuse": {
            "description": "Your email for abuse notifications.",
            "type": "string"
          },
          "disable_reset": {
            "description": "Set to `true` to disable account resets, or `false` to enable them.",
            "type": "boolean"
          },
          "disable_reinstall": {
            "description": "Set to `true` to disable server reinstalls, or `false` to enable them.",
            "type": "boolean"
          },
          "disable_server_notifications": {
            "description": "Set to `true` to disable server notifications, or `false` to enable them.",
            "type": "boolean"
          },
          "disable_email_notifications": {
            "description": "Set to `true` to disable email notifications, or `false` to enable them.",
            "type": "boolean"
          },
          "gstin": {
            "description": "Your GST identification number (if applicable).",
            "type": "string"
          }
        },
        "example": {
          "name": "John Doe",
          "company": "My Company",
          "address": "124 My St",
          "address2": "",
          "city": "My Town",
          "state": "PA",
          "zip": "17522",
          "country": "US",
          "phone": "8675309",
          "locale": "",
          "email_invoices": "",
          "email_abuse": "",
          "disable_reset": false,
          "disable_reinstall": false,
          "disable_server_notifications": false,
          "disable_email_notifications": false,
          "gstin": ""
        }
      },
      "AccountSshKey": {
        "title": "AccountSshKey",
        "description": "SSH Keys",
        "type": "object",
        "properties": {
          "ssh_key": {
            "type": "string"
          }
        },
        "example": {
          "ssh_key": ""
        }
      },
      "AffiliateBannerRow": {
        "title": "AffiliateBannerRow",
        "description": "An affiliate banner image details.",
        "type": "object",
        "properties": {
          "image": {
            "type": "string"
          },
          "width": {
            "type": "string"
          },
          "height": {
            "type": "string"
          }
        },
        "example": {
          "image": "12946798.gif",
          "width": "125",
          "height": "125"
        }
      },
      "AffiliateTrafficRow": {
        "title": "AffiliateTrafficRow",
        "description": "Affiliate Web Traffic Entry",
        "type": "object",
        "properties": {
          "traffic_id": {
            "type": "string"
          },
          "traffic_ip": {
            "type": "string"
          },
          "traffic_url": {
            "type": "string"
          },
          "traffic_affiliate": {
            "type": "string"
          },
          "traffic_referrer": {
            "type": "string"
          },
          "traffic_timestamp": {
            "type": "string"
          }
        },
        "example": {
          "traffic_id": "91839913",
          "traffic_ip": "2a06:98c0:3600::",
          "traffic_url": "https://www.interserver.net/webhosting/?id=8",
          "traffic_affiliate": "8",
          "traffic_referrer": "",
          "traffic_timestamp": "2023-09-30 06:30:27"
        }
      },
      "Backup": {
        "description": "Full detail view of a backup service including billing, service info, and configuration.",
        "type": "object",
        "properties": {
          "serviceInfo": {
            "$ref": "#/components/schemas/BackupServiceInfo"
          },
          "client_links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BackupClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/BackupBillingDetails"
          },
          "custCurrency": {
            "description": "Customer's currency.",
            "type": "string",
            "example": "USD"
          },
          "custCurrencySymbol": {
            "description": "Customer's currency symbol.",
            "type": "string",
            "example": "$"
          },
          "serviceMaster": {
            "$ref": "#/components/schemas/BackupServiceMaster"
          },
          "package": {
            "description": "Package information.",
            "type": "string"
          },
          "serviceExtra": {
            "$ref": "#/components/schemas/BackupServiceExtra"
          },
          "extraInfoTables": {
            "$ref": "#/components/schemas/BackupExtraInfoTables"
          }
        }
      },
      "BackupBillingDetails": {
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "description": "Last invoice date of the service.",
            "type": "string",
            "example": "December 29, 2021"
          },
          "service_payment_status": {
            "description": "Payment status of the service.",
            "type": "string",
            "example": "Paid"
          },
          "service_frequency": {
            "description": "Billing frequency of the service.",
            "type": "string",
            "example": "Monthly"
          },
          "next_date": {
            "description": "Next billing date of the service.",
            "type": "string",
            "example": "2022-01-29T14:09:57.000Z"
          },
          "service_next_invoice_date": {
            "description": "Next invoice date of the service.",
            "type": "string",
            "example": "January 29, 2022"
          },
          "service_currency": {
            "description": "Currency of the service.",
            "type": "string",
            "example": "USD"
          },
          "service_currency_symbol": {
            "description": "Currency symbol of the service.",
            "type": "string",
            "example": "$"
          },
          "service_cost_info": {
            "description": "Cost information of the service.",
            "type": "string",
            "example": "3"
          },
          "service_extra": {
            "description": "Service Extra Info",
            "type": "string",
            "example": "[]"
          },
          "service_extra_json": {
            "description": "JSON representation of extra service information.",
            "type": "string",
            "example": "[]"
          }
        }
      },
      "BackupClientLink": {
        "description": "A navigation link for backup service actions in the client portal.",
        "type": "object",
        "properties": {
          "label": {
            "description": "Label of the client link.",
            "type": "string"
          },
          "link": {
            "description": "Link URL of the client link.",
            "type": "string"
          },
          "icon": {
            "description": "Icon of the client link.",
            "type": "string"
          },
          "icon_text": {
            "description": "Icon text of the client link.",
            "type": "string"
          },
          "help_text": {
            "description": "Help text of the client link.",
            "type": "string"
          },
          "other_attr": {
            "description": "Other attributes of the client link.",
            "type": "string"
          }
        }
      },
      "BackupExtraInfoTables": {
        "description": "Supplementary information tables for a backup service (IP info, etc.).",
        "type": "object",
        "properties": {
          "ip_info": {
            "$ref": "#/components/schemas/BackupIPInfo"
          }
        }
      },
      "BackupIPInfo": {
        "description": "IP address information table for a backup service.",
        "type": "object",
        "properties": {
          "title": {
            "description": "Title of the IP information table.",
            "type": "string",
            "example": "IP Information"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BackupIPInfoRow"
            }
          }
        }
      },
      "BackupIPInfoRow": {
        "description": "A single row in the backup IP information table.",
        "type": "object",
        "properties": {
          "desc": {
            "description": "Description of the IP information.",
            "type": "string",
            "example": "Netmask"
          },
          "value": {
            "description": "Value of the IP information.",
            "type": "string",
            "example": "255.255.255.248"
          }
        }
      },
      "BackupRow": {
        "title": "BackupRow",
        "description": "A result row from the `Backups` `GET` request.",
        "type": "object",
        "properties": {
          "backup_id": {
            "description": "The id of the backup.",
            "type": "string",
            "example": "2414"
          },
          "backup_name": {
            "description": "The name of the backup.",
            "type": "string",
            "example": "storage-nj.interserver.net"
          },
          "backup_cost": {
            "description": "The cost of the backup.",
            "type": "string",
            "example": "5.99"
          },
          "backup_username": {
            "description": "The username of the backup.",
            "type": "string",
            "example": "detainin2414"
          },
          "backup_status": {
            "description": "The status of the backup.",
            "type": "string",
            "example": "pending-setup"
          },
          "services_name": {
            "description": "The services name of the backup.",
            "type": "string",
            "example": "Swift Storage"
          }
        },
        "example": {
          "backup_id": "2414",
          "backup_name": "storage-nj.interserver.net",
          "backup_cost": "5.99",
          "backup_username": "detainin2414",
          "backup_status": "pending-setup",
          "services_name": "Swift Storage"
        }
      },
      "BackupLoginResponse": {
        "title": "BackupLoginResponse",
        "description": "Login session response for backup storage.",
        "type": "object",
        "properties": {
          "success": {
            "description": "Indicates whether a login session was created.",
            "type": "boolean"
          },
          "text": {
            "description": "Login URL or error text returned by the storage provider.",
            "type": "string"
          }
        }
      },
      "BackupServiceExtra": {
        "type": "string"
      },
      "BackupServiceInfo": {
        "description": "Core service record for a backup storage service including ID, status, quota, and billing details.",
        "type": "object",
        "properties": {
          "backup_id": {
            "description": "Backup ID.",
            "type": "string",
            "example": "21163"
          },
          "backup_server": {
            "description": "Backup server ID.",
            "type": "string",
            "example": "38"
          },
          "backup_username": {
            "description": "Backup username.",
            "type": "string",
            "example": "st21163"
          },
          "backup_type": {
            "description": "Backup type.",
            "type": "string",
            "example": "10831"
          },
          "backup_currency": {
            "description": "Backup currency.",
            "type": "string",
            "example": "USD"
          },
          "backup_order_date": {
            "description": "Backup order date.",
            "type": "string",
            "example": "2021-12-29T14:09:57.000Z"
          },
          "backup_custid": {
            "description": "Backup customer ID.",
            "type": "string",
            "example": "2773"
          },
          "backup_quota": {
            "description": "Backup quota.",
            "type": "string",
            "example": "0"
          },
          "backup_ip": {
            "description": "Backup IP address.",
            "type": "string",
            "example": "64.20.55.234"
          },
          "backup_status": {
            "description": "Backup status.",
            "type": "string",
            "example": "canceled"
          },
          "backup_invoice": {
            "description": "Backup invoice.",
            "type": "string",
            "example": "19591007"
          },
          "backup_coupon": {
            "description": "Backup coupon.",
            "type": "string",
            "example": "0"
          },
          "backup_extra": {
            "description": "Backup extra information.",
            "type": "string",
            "example": "[]"
          },
          "backup_server_status": {
            "description": "Backup server status.",
            "type": "string",
            "example": "deleted"
          },
          "backup_comment": {
            "description": "Backup comment.",
            "type": "string"
          }
        }
      },
      "BackupServiceMaster": {
        "type": "object",
        "properties": {
          "backup_id": {
            "description": "Backup ID of the service master.",
            "type": "integer",
            "example": 38
          },
          "backup_name": {
            "description": "Name of the backup service.",
            "type": "string",
            "example": "storage1400.is.cc"
          },
          "backup_ip": {
            "description": "IP address of the backup service.",
            "type": "string",
            "example": "44.22.11.88"
          },
          "backup_type": {
            "description": "Type of the backup service.",
            "type": "integer",
            "example": 703
          },
          "backup_hdsize": {
            "description": "Size of the backup service's hard drive.",
            "type": "integer",
            "example": 156448
          },
          "backup_hdfree": {
            "description": "Amount of free space on the backup service's hard drive.",
            "type": "integer",
            "example": 61374
          },
          "backup_last_update": {
            "description": "Last update timestamp of the backup service.",
            "type": "string",
            "example": "2023-08-17T23:20:02.000Z"
          },
          "backup_available": {
            "description": "Availability status of the backup service.",
            "type": "integer",
            "example": 0
          },
          "backup_iowait": {
            "description": "I/O wait status of the backup service.",
            "type": "integer",
            "example": 0
          },
          "backup_order": {
            "description": "Order associated with the backup service.",
            "type": "integer",
            "example": 21359
          }
        }
      },
      "BackupsOrder": {
        "description": "Available backup storage packages and pricing for ordering a new backup service.",
        "required": [
          "packageCosts",
          "serviceTypes"
        ],
        "type": "object",
        "properties": {
          "packageCosts": {
            "$ref": "#/components/schemas/BackupsOrderPackageCosts"
          },
          "serviceTypes": {
            "type": "object",
            "properties": {
              "11006": {
                "$ref": "#/components/schemas/BackupsOrderServiceTypes"
              }
            }
          }
        }
      },
      "BackupsOrderPackageCosts": {
        "required": [
          "11006"
        ],
        "type": "object",
        "properties": {
          "11006": {
            "description": "The cost associated with package 11006.",
            "type": "integer",
            "example": 84
          }
        }
      },
      "BackupsOrderServiceTypes": {
        "required": [
          "services_id",
          "services_name",
          "services_cost",
          "services_category",
          "services_buyable",
          "services_type",
          "services_field1",
          "services_field2",
          "services_module"
        ],
        "type": "object",
        "properties": {
          "services_id": {
            "description": "The ID of the service.",
            "type": "string",
            "example": "11006"
          },
          "services_name": {
            "description": "The name of the service.",
            "type": "string",
            "example": "Storage ST 700"
          },
          "services_cost": {
            "description": "The cost of the service.",
            "type": "string",
            "example": "84.00"
          },
          "services_category": {
            "description": "The category of the service.",
            "type": "string",
            "example": "702"
          },
          "services_buyable": {
            "description": "Indicates if the service is buyable.",
            "type": "string",
            "example": "1"
          },
          "services_type": {
            "description": "The type of the service.",
            "type": "string",
            "example": "703"
          },
          "services_field1": {
            "description": "Field 1 of the service.",
            "type": "string",
            "example": ""
          },
          "services_field2": {
            "description": "Field 2 of the service.",
            "type": "string",
            "example": "bandwidth=40000000,quota=40000000,inode=100,uinode=ON,vdomains=2,nsubdomains=5,nemails=0,nemailf=0,nemailml=0,nemailr=0,mysql=5,domainptr=5,ftp=100,aftp=OFF,cgi=ON,php=ON,spam=OFF,cron=OFF,catchall=OFF,ssl=ON,ssh=ON,sysinfo=ON,login_keys=ON,dnscontrol=OFF,suspend_at_limit=ON"
          },
          "services_module": {
            "description": "The module of the service.",
            "type": "string",
            "example": "backups"
          }
        }
      },
      "BillingAddCcRequest": {
        "title": "BillingAddCcRequest",
        "description": "Request to add a new creditcard into the system.",
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "cc": {
            "type": "string"
          },
          "cc_exp": {
            "type": "string"
          },
          "cc_ccv2": {
            "type": "string"
          }
        },
        "example": {
          "name": "John Doe",
          "address": "1 Somewhere St.",
          "city": "Secaucus",
          "state": "NJ",
          "country": "US",
          "zip": "07013",
          "cc": "1234556781923121",
          "cc_exp": "12/2023",
          "cc_ccv2": "123"
        }
      },
      "BillingPrepayRequest": {
        "title": "BillingPrepayRequest",
        "description": "Request payload for creating a new prepay balance.",
        "type": "object",
        "properties": {
          "module": {
            "description": "Module the prepay should be applied to (for example `default`).",
            "type": "string"
          },
          "amount": {
            "description": "Amount to add to prepay balance. Minimum is $10.",
            "type": "number"
          },
          "automatic_use": {
            "description": "Whether the prepay balance should be used automatically.",
            "type": "string",
            "example": "1"
          }
        }
      },
      "BillingInvoiceList": {
        "title": "BillingInvoiceList",
        "description": "Summary list of invoices for the account.",
        "type": "object",
        "properties": {
          "rows": {
            "description": "Invoice rows returned for the account.",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "summary": {
            "description": "Totals and summary data for the invoices list.",
            "type": "object"
          }
        }
      },
      "BillingInvoiceDetail": {
        "title": "BillingInvoiceDetail",
        "description": "Detailed invoice data payload returned for a single invoice.",
        "type": "object",
        "additionalProperties": {
          "description": "The invoice payload varies by invoice type and includes line item details.",
          "type": "string"
        }
      },
      "BillingVerifyCcRequest": {
        "title": "BillingVerifyCcRequest",
        "description": "Payload for verifying a credit card through the verification flow.",
        "type": "object",
        "properties": {
          "idx": {
            "description": "Card index to verify.",
            "type": "integer"
          },
          "cc_ccv2": {
            "description": "CVV code for verification.",
            "type": "string"
          },
          "cc_amount1": {
            "description": "First micro-charge amount for verification.",
            "type": "string"
          },
          "cc_amount2": {
            "description": "Second micro-charge amount for verification.",
            "type": "string"
          },
          "terms": {
            "description": "Whether terms were accepted for verification.",
            "type": "boolean"
          }
        }
      },
      "BillingPaymentMethodRequest": {
        "title": "BillingPaymentMethodRequest",
        "description": "Payload for updating the default account payment method.",
        "type": "object",
        "properties": {
          "payment_method": {
            "description": "Payment method identifier (cc, paypal, or cc{index}).",
            "type": "string"
          },
          "cc_auto": {
            "description": "Whether automatic credit card payments are enabled.",
            "type": "string"
          }
        }
      },
      "CaptchaResponse": {
        "title": "CaptchaResponse",
        "description": "A base-64 encoded captcha image.",
        "required": [
          "captcha"
        ],
        "type": "object",
        "properties": {
          "captcha": {
            "description": "The base64 encoded captcha image.",
            "type": "string",
            "example": "data:image/jpeg;base64,/9j/4AAQ"
          }
        },
        "example": {
          "captcha": "data:image/jpeg;base64,/9j/4AAQ"
        }
      },
      "ChargeInvoiceRows": {
        "description": "Billing invoices associated with a service, including invoice IDs, descriptions, amounts, and payment status.",
        "type": "object",
        "properties": {
          "success": {
            "description": "Whether the invoice retrieval was successful.",
            "type": "boolean"
          },
          "invoices": {
            "description": "List of invoices for the service.",
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "invoices_id": {
                  "description": "Unique invoice ID.",
                  "type": "number"
                },
                "invoices_description": {
                  "description": "Description of the invoice charge.",
                  "type": "string"
                },
                "invoices_amount": {
                  "description": "Invoice amount.",
                  "type": "number"
                },
                "invoices_date": {
                  "description": "Invoice date.",
                  "type": "string"
                },
                "invoices_paid": {
                  "enum": [
                    0,
                    1
                  ],
                  "type": "number"
                },
                "invoices_due_date": {
                  "type": "string"
                },
                "invoices_currency": {
                  "type": "string"
                },
                "currency_symbol": {
                  "type": "string"
                },
                "invoices_date_formatted": {
                  "type": "string"
                },
                "paid_invoices": {
                  "description": "This is optional when invoices_paid = 1 this array will show",
                  "type": "object",
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "invoices_id": {
                        "type": "string"
                      },
                      "invoices_description": {
                        "type": "string"
                      },
                      "invoices_amount": {
                        "type": "number"
                      },
                      "invoices_date": {
                        "type": "string"
                      },
                      "invoices_currency": {
                        "type": "string"
                      },
                      "currency_symbol": {
                        "type": "string"
                      },
                      "invoices_date_formatted": {
                        "type": "string"
                      },
                      "payment_type": {
                        "type": "string"
                      },
                      "refund_invoices": {
                        "description": "This is optional when refund is present this will show",
                        "type": "object",
                        "additionalProperties": {
                          "type": "object",
                          "properties": {
                            "invoices_id": {
                              "type": "number"
                            },
                            "invoices_description": {
                              "type": "string"
                            },
                            "invoices_amount": {
                              "type": "number"
                            },
                            "invoices_date": {
                              "type": "string"
                            },
                            "invoices_currency": {
                              "type": "string"
                            },
                            "currency_symbol": {
                              "type": "string"
                            },
                            "invoices_date_formatted": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "example": {
          "success": true,
          "invoices": {
            "1234565": {
              "invoices_id": 1234565,
              "invoices_description": "Current IP + Scrub",
              "invoices_amount": 5,
              "invoices_date": "2025-12-05T19:38:24.000Z",
              "invoices_paid": 1,
              "invoices_due_date": "2025-12-19T19:38:24.000Z",
              "invoices_currency": "USD",
              "currency_symbol": "$",
              "invoices_date_formatted": "5th Dec 2025",
              "paid_invoices": {
                "654321": {
                  "invoices_id": "654321,",
                  "invoices_description": "Credit Card Payment 2189347824",
                  "invoices_amount": 5,
                  "invoices_date": "2025-12-05T19:38:38.000Z",
                  "invoices_currency": "USD",
                  "currency_symbol": "$",
                  "invoices_date_formatted": "5th Dec 2025",
                  "payment_type": "Credit Card Payment",
                  "refund_invoices": {
                    "98765": {
                      "invoices_id": 98765,
                      "invoices_description": "REFUND: Credit Card Payment 2189347824",
                      "invoices_amount": 5,
                      "invoices_date": "2025-12-16T13:32:22.000Z",
                      "invoices_currency": "USD",
                      "currency_symbol": "$",
                      "invoices_date_formatted": "16th Dec 2025"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "DnsListItem": {
        "title": "DnsListItem",
        "description": "A DNS zone entry with its ID, domain name, and record content.",
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "content": {
            "type": "string"
          }
        },
        "additionalProperties": false,
        "example": {
          "id": 68,
          "name": "hussfamily.com",
          "content": "64.20.35.186"
        }
      },
      "DnsNewDomain": {
        "title": "DnsNewDomain",
        "description": "The request for a new domain to be managed by the dns servers.",
        "required": [
          "domain",
          "ip"
        ],
        "type": "object",
        "properties": {
          "domain": {
            "description": "The domain name.",
            "type": "string",
            "example": "mydomain.com"
          },
          "ip": {
            "description": "IP Address to point the domain to.",
            "type": "string",
            "example": "1.2.3.4"
          }
        },
        "example": {
          "domain": "mydomain.com",
          "ip": "1.2.3.4"
        }
      },
      "DnsRecord": {
        "title": "DnsRecord",
        "description": "A single DNS Record row for a Domain",
        "required": [
          "type",
          "ttl",
          "ordername",
          "id",
          "auth",
          "content",
          "disabled",
          "domain_id",
          "name",
          "prio"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "The ID of the DNS Record.",
            "type": "string",
            "example": "472"
          },
          "domain_id": {
            "description": "The ID of the Domain this is a record of.",
            "type": "string",
            "example": "68"
          },
          "name": {
            "type": "string",
            "example": "hussfamily.com"
          },
          "type": {
            "$ref": "#/components/schemas/DnsRecordType"
          },
          "content": {
            "description": "The content of the record, such as the IP address or hsotname.",
            "type": "string",
            "example": "cdns1.interserver.net"
          },
          "ttl": {
            "description": "Time To Live (seconds)",
            "type": "string",
            "example": "86400"
          },
          "prio": {
            "description": "Priority",
            "type": "string",
            "example": "0"
          },
          "disabled": {
            "type": "string",
            "example": "0"
          },
          "ordername": {
            "description": "Alternate name to use for sorting",
            "type": "string"
          },
          "auth": {
            "type": "string",
            "example": "1"
          }
        },
        "example": {
          "id": "473",
          "domain_id": "68",
          "name": "hussfamily.com",
          "type": "NS",
          "content": "cdns1.interserver.net",
          "ttl": "86400",
          "prio": "0",
          "disabled": "0",
          "ordername": "",
          "auth": "1"
        }
      },
      "DnsUpdateRecord": {
        "title": "DnsUpdateRecord",
        "description": "The request data to update a dns record.",
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/DnsRecordType"
          },
          "content": {
            "type": "string"
          },
          "ttl": {
            "type": "string"
          },
          "prio": {
            "type": "string"
          },
          "disabled": {
            "type": "string"
          },
          "ordername": {
            "type": "string"
          },
          "auth": {
            "type": "string"
          }
        },
        "example": {
          "name": "mydomain.com",
          "type": "NS",
          "content": "cdns1.interserver.net",
          "ttl": "86400",
          "prio": "0",
          "disabled": "0",
          "ordername": "",
          "auth": "1"
        }
      },
      "Domain": {
        "description": "Full detail view of a domain service including billing, contacts, DNS, and configuration.",
        "type": "object",
        "properties": {
          "serviceInfo": {
            "$ref": "#/components/schemas/DomainServiceInfo"
          },
          "serviceTypes": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/DomainServiceType"
            }
          },
          "client_links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DomainClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/DomainBillingDetails"
          },
          "custCurrency": {
            "type": "string"
          },
          "custCurrencySymbol": {
            "type": "string"
          },
          "serviceExtra": {
            "$ref": "#/components/schemas/DomainBillingExtra"
          },
          "extraInfoTables": {
            "$ref": "#/components/schemas/BackupExtraInfoTables"
          },
          "serviceType": {
            "$ref": "#/components/schemas/DomainServiceType"
          },
          "contact_details": {
            "$ref": "#/components/schemas/DomainContactDetails"
          },
          "pwarning": {
            "type": "string"
          },
          "transfer_info": {
            "type": "string"
          },
          "errors": {
            "type": "boolean"
          },
          "domain_logs": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allInfo": {
            "$ref": "#/components/schemas/DomainAllInfo"
          },
          "registrarStatus": {
            "type": "string"
          },
          "locked": {
            "type": "string"
          },
          "whoisPrivacy": {
            "type": "string"
          },
          "autoRenew": {
            "type": "string"
          }
        }
      },
      "DomainAdminContact": {
        "description": "Administrative contact information for a domain registration.",
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "org_name": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "postal_code": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "fax": {
            "type": "string"
          },
          "address2": {
            "type": "string"
          },
          "address3": {
            "type": "string"
          },
          "address1": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          }
        }
      },
      "DomainAllInfo": {
        "description": "Complete domain registration information returned from the registrar, including contacts, nameservers, and expiry dates.",
        "type": "object",
        "properties": {
          "_OPS_version": {
            "type": "string"
          },
          "attributes": {
            "type": "object",
            "properties": {
              "contact_set": {
                "type": "object",
                "properties": {
                  "owner": {
                    "$ref": "#/components/schemas/DomainOwnerContact"
                  },
                  "admin": {
                    "$ref": "#/components/schemas/DomainAdminContact"
                  },
                  "tech": {
                    "$ref": "#/components/schemas/DomainTechContact"
                  }
                }
              },
              "registry_createdate": {
                "type": "string"
              },
              "registry_expiredate": {
                "type": "string"
              },
              "tld_data": {
                "type": "string"
              },
              "let_expire": {
                "type": "string"
              },
              "auto_renew": {
                "type": "string"
              },
              "sponsoring_rsp": {
                "type": "string"
              },
              "gdpr_consent_status": {
                "type": "string"
              },
              "nameserver_list": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/DomainNameServer"
                }
              },
              "registry_updatedate": {
                "type": "string"
              },
              "affiliate_id": {
                "type": "string"
              },
              "expiredate": {
                "type": "string"
              }
            }
          },
          "object": {
            "type": "string"
          },
          "protocol": {
            "type": "string"
          },
          "response_text": {
            "type": "string"
          },
          "response_code": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "is_success": {
            "type": "string"
          }
        }
      },
      "DomainBillingDetails": {
        "description": "Billing information for a domain service including payment status, dates, and cost.",
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "description": "Date of the last invoice for the domain.",
            "type": "string"
          },
          "service_payment_status": {
            "description": "Current payment status (e.g., Paid, Unpaid).",
            "type": "string"
          },
          "service_frequency": {
            "description": "Billing frequency (e.g., Yearly, Monthly).",
            "type": "string"
          },
          "next_date": {
            "description": "Next billing date (ISO 8601).",
            "type": "string"
          },
          "service_next_invoice_date": {
            "description": "Human-readable next invoice date.",
            "type": "string"
          },
          "service_currency": {
            "description": "Billing currency code.",
            "type": "string"
          },
          "service_currency_symbol": {
            "description": "Billing currency symbol.",
            "type": "string"
          },
          "service_cost_info": {
            "description": "Cost breakdown information.",
            "type": "string"
          },
          "service_extra": {
            "$ref": "#/components/schemas/DomainBillingExtra"
          },
          "service_extra_json": {
            "description": "Raw JSON string of extra billing data.",
            "type": "string"
          }
        }
      },
      "DomainBillingExtra": {
        "description": "Extended billing context for a domain including registrar order details and contact info.",
        "type": "object",
        "properties": {
          "order": {
            "$ref": "#/components/schemas/DomainOrderResponse"
          },
          "order_id": {
            "type": "string"
          },
          "domain_id": {
            "type": "string"
          },
          "provProcessPending": {
            "$ref": "#/components/schemas/DomainProvProcessPending"
          },
          "email": {
            "type": "string"
          },
          "firstname": {
            "type": "string"
          },
          "lastname": {
            "type": "string"
          },
          "company": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "address2": {
            "type": "string"
          },
          "address3": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "fax": {
            "type": "string"
          }
        }
      },
      "DomainClientLink": {
        "description": "Links and labels for domain-related UI actions.",
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "link": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "icon_text": {
            "type": "string"
          },
          "help_text": {
            "type": "string"
          }
        }
      },
      "DomainContactDetails": {
        "description": "Contact details used for domain registrant/admin/technical/billing records.",
        "type": "object",
        "properties": {
          "status": {
            "description": "Contact status reported by the registrar.",
            "type": "string"
          },
          "state": {
            "description": "State or region for the contact address.",
            "type": "string"
          },
          "org_name": {
            "description": "Organization name for the contact.",
            "type": "string"
          },
          "country": {
            "description": "Two-letter country code for the contact.",
            "type": "string"
          },
          "postal_code": {
            "description": "Postal or ZIP code for the contact address.",
            "type": "string"
          },
          "email": {
            "description": "Email address for the contact.",
            "type": "string"
          },
          "fax": {
            "description": "Fax number for the contact, if available.",
            "type": "string"
          },
          "address2": {
            "description": "Secondary street address line.",
            "type": "string"
          },
          "address3": {
            "description": "Tertiary street address line.",
            "type": "string"
          },
          "address1": {
            "description": "Primary street address line.",
            "type": "string"
          },
          "city": {
            "description": "City for the contact address.",
            "type": "string"
          },
          "phone": {
            "description": "Phone number for the contact.",
            "type": "string"
          },
          "first_name": {
            "description": "First name for the contact.",
            "type": "string"
          },
          "last_name": {
            "description": "Last name for the contact.",
            "type": "string"
          }
        }
      },
      "DomainNameServer": {
        "description": "A single registered nameserver entry with glue IPs.",
        "type": "object",
        "properties": {
          "sortorder": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "ipaddress": {
            "type": "string"
          }
        }
      },
      "DomainDnssecRequest": {
        "description": "Request payload for adding DNSSEC DS records to a domain.",
        "type": "object",
        "properties": {
          "algorithm": {
            "description": "List of DNSSEC algorithm IDs for each record.",
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "digest_type": {
            "description": "List of digest type IDs for each record.",
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "digest": {
            "description": "List of hex digests for each record.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "key_tag": {
            "description": "List of key tag values corresponding to each record.",
            "type": "array",
            "items": {
              "type": "integer"
            }
          }
        }
      },
      "DomainDnssecRecords": {
        "description": "DNSSEC DS records currently configured for a domain.",
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "algorithm": {
              "type": "string"
            },
            "digest_type": {
              "type": "string"
            },
            "digest": {
              "type": "string"
            },
            "key_tag": {
              "type": "string"
            }
          }
        }
      },
      "DomainLookupResponse": {
        "description": "Availability, pricing, and order-field metadata for a domain lookup.",
        "type": "object",
        "properties": {
          "available": {
            "description": "Whether the domain is available to register.",
            "type": "boolean"
          },
          "premium": {
            "description": "Whether the domain is marked as premium by the registrar.",
            "type": "boolean"
          },
          "website": {
            "description": "Indicates if the domain is already used by a website service.",
            "type": "boolean"
          },
          "domain_service": {
            "description": "Indicates if the domain already exists as a domain service on the account.",
            "type": "boolean"
          },
          "service": {
            "description": "Service catalog details for the domain's TLD.",
            "type": "object"
          },
          "whois_privacy": {
            "description": "Whether Whois privacy is available for the TLD.",
            "type": "boolean"
          },
          "new": {
            "description": "Calculated registration price, when available.",
            "type": "string"
          },
          "renewal": {
            "description": "Calculated renewal price, when available.",
            "type": "string"
          },
          "transfer": {
            "description": "Calculated transfer price, when available.",
            "type": "string"
          },
          "fields": {
            "description": "Registrar field requirements for this domain/TLD.",
            "type": "object"
          },
          "currencies": {
            "description": "Pricing information normalized to supported currencies.",
            "type": "object"
          }
        }
      },
      "DomainSearchResponse": {
        "description": "Lookup and suggestion results returned for a domain search.",
        "type": "object",
        "properties": {
          "success": {
            "description": "Indicates whether the registrar search succeeded.",
            "type": "boolean"
          },
          "response_text": {
            "description": "Human-readable status text from the registrar.",
            "type": "string"
          },
          "response_time": {
            "description": "Response time as reported by the registrar.",
            "type": "string"
          },
          "lookup": {
            "description": "Availability lookup results for queried domains.",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "suggest": {
            "description": "Suggested alternative domains and availability data.",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "tlds": {
            "description": "TLDs evaluated during the search.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "DomainOrder": {
        "description": "Ordering metadata for domain registration and transfers.",
        "type": "object",
        "properties": {
          "whoisPrivacyCost": {
            "description": "Cost of Whois Privacy",
            "type": "string",
            "example": "5"
          },
          "services": {
            "description": "Available domain service catalog entries keyed by service ID.",
            "type": "object",
            "properties": {
              "DomainOrderServices10001": {
                "$ref": "#/components/schemas/DomainOrderServices10001"
              }
            }
          },
          "tldServices": {
            "description": "Map of TLDs to domain service IDs.",
            "type": "object"
          }
        }
      },
      "DomainOrderResponse": {
        "description": "Registrar response metadata returned after a domain order.",
        "type": "object",
        "properties": {
          "_OPS_version": {
            "type": "string"
          },
          "protocol": {
            "type": "string"
          },
          "is_success": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "attributes": {
            "type": "object",
            "properties": {
              "id": {
                "description": "Registrar order ID.",
                "type": "string"
              },
              "admin_email": {
                "description": "Administrative contact email provided for the order.",
                "type": "string"
              }
            }
          },
          "response_text": {
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "response_code": {
            "type": "string"
          }
        }
      },
      "DomainOrderServices10001": {
        "description": "Example schema for a domain service catalog entry.",
        "type": "object",
        "properties": {
          "services_id": {
            "description": "Service ID",
            "type": "string",
            "example": "10001"
          },
          "services_name": {
            "description": "Service Name",
            "type": "string",
            "example": ".asia Domain Name Registration"
          },
          "services_cost": {
            "description": "Service Cost",
            "type": "string",
            "example": "19.00"
          },
          "services_currency": {
            "description": "Currency of Service Cost",
            "type": "string",
            "example": "USD"
          },
          "services_category": {
            "description": "Service Category",
            "type": "string",
            "example": "100"
          },
          "services_buyable": {
            "description": "Buyable flag for Service",
            "type": "string",
            "example": "1"
          },
          "services_type": {
            "description": "Service Type",
            "type": "string",
            "example": "100"
          },
          "services_field1": {
            "description": "Field 1 of Service",
            "type": "string",
            "example": ".asia"
          },
          "services_field2": {
            "description": "Field 2 of Service",
            "type": "string",
            "example": ""
          },
          "services_module": {
            "description": "Module of Service",
            "type": "string",
            "example": "domains"
          },
          "services_hidden": {
            "description": "Hidden flag for Service",
            "type": "string",
            "example": "0"
          }
        }
      },
      "DomainOwnerContact": {
        "description": "Contact details for the registered domain owner.",
        "type": "object",
        "properties": {
          "postal_code": {
            "type": "string"
          },
          "org_name": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "address3": {
            "type": "string"
          },
          "fax": {
            "type": "string"
          },
          "address2": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "address1": {
            "type": "string"
          }
        }
      },
      "DomainProvProcessPending": {
        "description": "Provisioning status data for a pending domain order.",
        "type": "object",
        "properties": {
          "_OPS_version": {
            "type": "string"
          },
          "response_text": {
            "type": "string"
          },
          "protocol": {
            "type": "string"
          },
          "response_code": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "object": {
            "type": "string"
          },
          "is_success": {
            "type": "string"
          },
          "attributes": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "order_id": {
                "type": "string"
              },
              "registration expiration date": {
                "type": "string"
              },
              "f_auto_renew": {
                "type": "string"
              }
            }
          }
        }
      },
      "DomainRow": {
        "title": "DomainRow",
        "description": "A result row from the `Domains` `GET` request.",
        "type": "object",
        "properties": {
          "domain_id": {
            "description": "The ID number of the domain in our billing system.",
            "type": "string",
            "example": "59237"
          },
          "domain_hostname": {
            "description": "The hostname of the domain.",
            "type": "string",
            "example": "mydomain.com"
          },
          "domain_expire_date": {
            "description": "The expiration date of the domain.",
            "type": "string",
            "example": "2023-08-14T00:59:38.000Z"
          },
          "cost": {
            "description": "The cost of the domain.",
            "type": "string",
            "example": "11.00"
          },
          "domain_status": {
            "description": "The billing / registration status of the domain.",
            "type": "string",
            "example": "active"
          }
        },
        "example": {
          "domain_id": "592337",
          "domain_hostname": "mydomain.com",
          "domain_expire_date": "2023-08-14T00:59:38.000Z",
          "cost": "18.00",
          "domain_status": "active"
        }
      },
      "DomainServiceInfo": {
        "description": "Detailed domain service record for a domain order.",
        "type": "object",
        "properties": {
          "domain_id": {
            "type": "string"
          },
          "domain_hostname": {
            "type": "string"
          },
          "domain_username": {
            "type": "string"
          },
          "domain_password": {
            "type": "string"
          },
          "domain_type": {
            "type": "string"
          },
          "domain_currency": {
            "type": "string"
          },
          "domain_expire_date": {
            "type": "string"
          },
          "domain_order_date": {
            "type": "string"
          },
          "domain_custid": {
            "type": "string"
          },
          "domain_status": {
            "type": "string"
          },
          "domain_invoice": {
            "type": "string"
          },
          "domain_coupon": {
            "type": "string"
          }
        }
      },
      "DomainWhoisPrivacyRequest": {
        "description": "Request payload for enabling or disabling Whois privacy.",
        "type": "object",
        "properties": {
          "func": {
            "description": "Action to perform (enable or disableCancel).",
            "type": "string",
            "example": "enable"
          },
          "csrf_token": {
            "description": "CSRF token if the API requires it for the account.",
            "type": "string"
          },
          "domain_firstname": {
            "type": "string"
          },
          "domain_lastname": {
            "type": "string"
          },
          "domain_email": {
            "type": "string"
          },
          "domain_address": {
            "type": "string"
          },
          "domain_address2": {
            "type": "string"
          },
          "domain_address3": {
            "type": "string"
          },
          "domain_city": {
            "type": "string"
          },
          "domain_state": {
            "type": "string"
          },
          "domain_zip": {
            "type": "string"
          },
          "domain_country": {
            "type": "string"
          },
          "domain_phone": {
            "type": "string"
          },
          "domain_fax": {
            "type": "string"
          },
          "domain_company": {
            "type": "string"
          },
          "domain_extra": {
            "type": "string"
          }
        }
      },
      "DomainServiceType": {
        "description": "Service type definition for a domain registration, including name, pricing, and category.",
        "type": "object",
        "properties": {
          "services_id": {
            "description": "Service type ID.",
            "type": "string"
          },
          "services_name": {
            "description": "Service type name (e.g., .com Registration).",
            "type": "string"
          },
          "services_cost": {
            "description": "Service cost per billing period.",
            "type": "string"
          },
          "services_category": {
            "description": "Service category ID.",
            "type": "string"
          },
          "services_buyable": {
            "description": "Whether this service type is available for purchase.",
            "type": "string"
          },
          "services_type": {
            "description": "Internal service type identifier.",
            "type": "string"
          },
          "services_field1": {
            "description": "TLD or first configurable field for the service type.",
            "type": "string"
          },
          "services_field2": {
            "description": "Second configurable field for the service type.",
            "type": "string"
          },
          "services_module": {
            "description": "Backend module handling this service type.",
            "type": "string"
          }
        }
      },
      "DomainTechContact": {
        "description": "Technical contact information for a domain registration.",
        "type": "object",
        "properties": {
          "state": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "org_name": {
            "type": "string"
          },
          "country": {
            "type": "string"
          },
          "postal_code": {
            "type": "string"
          },
          "fax": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "address2": {
            "type": "string"
          },
          "address3": {
            "type": "string"
          },
          "address1": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          }
        }
      },
      "GenericResponse": {
        "title": "GenericResponse",
        "description": "Generic Response",
        "required": [
          "text",
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "text": {
            "type": "string"
          }
        },
        "example": {
          "status": "ok",
          "text": "The command completed successfully."
        }
      },
      "Home": {
        "required": [
          "last_login_ip",
          "last_login",
          "currency",
          "amount",
          "invoice_list",
          "balance",
          "full_name",
          "email",
          "tickets",
          "ticketStatus",
          "ticketStatusView",
          "details",
          "services",
          "AFFILIATE_AMOUNT"
        ],
        "type": "object",
        "properties": {
          "last_login_ip": {
            "description": "Last login IP.",
            "type": "string",
            "example": "99.88.77.66"
          },
          "last_login": {
            "description": "Last login time.",
            "type": "string",
            "example": "14:58:pm - 17 Aug, 2023"
          },
          "currency": {
            "description": "Currency symbol.",
            "type": "string",
            "example": "$"
          },
          "amount": {
            "description": "Amount with currency.",
            "type": "string",
            "example": "$23.50"
          },
          "invoice_list": {
            "description": "Number of invoices.",
            "type": "integer",
            "example": 5
          },
          "balance": {
            "description": "Balance with currency.",
            "type": "string",
            "example": "$0.60"
          },
          "full_name": {
            "description": "Users full name.",
            "type": "string",
            "example": "John"
          },
          "email": {
            "description": "User email address.",
            "type": "string",
            "example": "user@domain.com"
          },
          "tickets": {
            "description": "List of tickets.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ticketStatus": {
            "description": "Ticket statuses.",
            "type": "object",
            "properties": {
              "Open": {
                "description": "Count of open tickets.",
                "type": "integer",
                "example": 4
              },
              "On Hold": {
                "description": "Count of tickets on hold.",
                "type": "integer",
                "example": 5
              }
            }
          },
          "ticketStatusView": {
            "description": "Ticket statuses with view numbers.",
            "type": "object",
            "properties": {
              "4": {
                "description": "Status corresponding to view number 4.",
                "type": "string",
                "example": "Open"
              },
              "5": {
                "description": "Status corresponding to view number 5.",
                "type": "string",
                "example": "On Hold"
              },
              "6": {
                "description": "Status corresponding to view number 6.",
                "type": "string",
                "example": "Closed"
              }
            }
          },
          "details": {
            "description": "Users details.",
            "type": "object",
            "properties": {
              "modules": {
                "type": "object",
                "properties": {
                  "domains": {
                    "$ref": "#/components/schemas/HomeDetailsModulesDomains"
                  },
                  "webhosting": {
                    "$ref": "#/components/schemas/HomeDetailsModulesWebhosting"
                  },
                  "vps": {
                    "$ref": "#/components/schemas/HomeDetailsModulesVps"
                  },
                  "licenses": {
                    "$ref": "#/components/schemas/HomeDetailsModulesLicenses"
                  },
                  "backups": {
                    "$ref": "#/components/schemas/HomeDetailsModulesBackups"
                  },
                  "servers": {
                    "$ref": "#/components/schemas/HomeDetailsModulesServers"
                  },
                  "quickservers": {
                    "$ref": "#/components/schemas/HomeDetailsModulesQuickservers"
                  }
                }
              }
            }
          },
          "services": {
            "description": "Users services.",
            "type": "object",
            "properties": {
              "domains": {
                "type": "object",
                "properties": {
                  "links": {
                    "$ref": "#/components/schemas/HomeServicesDomainsLinks"
                  },
                  "count": {
                    "description": "Number of domains.",
                    "type": "integer",
                    "example": 5
                  }
                }
              },
              "webhosting": {
                "type": "object",
                "properties": {
                  "links": {
                    "$ref": "#/components/schemas/HomeServicesWebhostingLinks"
                  },
                  "count": {
                    "description": "Number of web hosting services.",
                    "type": "integer",
                    "example": 8
                  }
                }
              },
              "vps": {
                "type": "object",
                "properties": {
                  "links": {
                    "$ref": "#/components/schemas/HomeServicesVpsLinks"
                  },
                  "count": {
                    "description": "Number of VPS services.",
                    "type": "integer",
                    "example": 20
                  }
                }
              },
              "licenses": {
                "type": "object",
                "properties": {
                  "links": {
                    "$ref": "#/components/schemas/HomeServicesLicensesLinks"
                  },
                  "count": {
                    "description": "Number of licenses.",
                    "type": "integer",
                    "example": 1
                  }
                }
              },
              "servers": {
                "type": "object",
                "properties": {
                  "links": {
                    "$ref": "#/components/schemas/HomeServicesServersLinks"
                  },
                  "count": {
                    "description": "Number of servers.",
                    "type": "integer",
                    "example": 1
                  }
                }
              },
              "backups": {
                "type": "object",
                "properties": {
                  "links": {
                    "description": "List of backup links.",
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "count": {
                    "description": "Number of backups.",
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          },
          "AFFILIATE_AMOUNT": {
            "description": "Affiliate amount with currency.",
            "type": "string",
            "example": "100"
          }
        }
      },
      "SearchAutocompleteResponse": {
        "description": "Autocomplete search results for the authenticated account.",
        "type": "object",
        "additionalProperties": {
          "description": "Search result fields vary based on service type.",
          "type": "object"
        }
      },
      "HomeDetailsModulesBackups": {
        "description": "Dashboard module configuration for backup storage services.",
        "type": "object",
        "properties": {
          "icon": {
            "description": "The icon for storages.",
            "type": "string",
            "example": "warehouse"
          },
          "view_link": {
            "description": "Link to view backup.",
            "type": "string",
            "example": "view_backup"
          },
          "heading": {
            "description": "Heading for storages.",
            "type": "string",
            "example": "Storages"
          },
          "buy_link": {
            "description": "Link to order storage.",
            "type": "string",
            "example": "order_storage"
          },
          "list_link": {
            "description": "Link to view backups list.",
            "type": "string",
            "example": "view_backups_list"
          }
        }
      },
      "HomeDetailsModulesDomains": {
        "description": "Dashboard module configuration for domain registration services.",
        "type": "object",
        "properties": {
          "icon": {
            "description": "The icon for domains.",
            "type": "string",
            "example": "globe"
          },
          "view_link": {
            "description": "Link to view domain.",
            "type": "string",
            "example": "view_domain"
          },
          "heading": {
            "description": "Heading for domains.",
            "type": "string",
            "example": "Domains"
          },
          "buy_link": {
            "description": "Link to order domain.",
            "type": "string",
            "example": "domain_order"
          },
          "list_link": {
            "description": "Link to view domains list.",
            "type": "string",
            "example": "view_domains_list"
          }
        }
      },
      "HomeDetailsModulesLicenses": {
        "description": "Dashboard module configuration for software license services.",
        "type": "object",
        "properties": {
          "icon": {
            "description": "The icon for licenses.",
            "type": "string",
            "example": "id-card"
          },
          "view_link": {
            "description": "Link to view license.",
            "type": "string",
            "example": "view_license"
          },
          "heading": {
            "description": "Heading for licenses.",
            "type": "string",
            "example": "Licenses"
          },
          "buy_link": {
            "description": "Link to order license.",
            "type": "string",
            "example": "order_license"
          },
          "list_link": {
            "description": "Link to view licenses list.",
            "type": "string",
            "example": "view_licenses_list"
          }
        }
      },
      "HomeDetailsModulesQuickservers": {
        "description": "Dashboard module configuration for QuickServer services.",
        "type": "object",
        "properties": {
          "icon": {
            "description": "The icon for quick servers.",
            "type": "string",
            "example": "database"
          },
          "view_link": {
            "description": "Link to view quick servers.",
            "type": "string",
            "example": "view_qs"
          },
          "heading": {
            "description": "Heading for quick servers.",
            "type": "string",
            "example": "Quick Servers"
          },
          "buy_link": {
            "description": "Link to order quick server.",
            "type": "string",
            "example": "order_quickserver"
          },
          "list_link": {
            "description": "Link to view quick servers list.",
            "type": "string",
            "example": "view_quickservers_list"
          }
        }
      },
      "HomeDetailsModulesServers": {
        "description": "Dashboard module configuration for dedicated server services.",
        "type": "object",
        "properties": {
          "icon": {
            "description": "The icon for dedicated servers.",
            "type": "string",
            "example": "server"
          },
          "view_link": {
            "description": "Link to view server.",
            "type": "string",
            "example": "view_server"
          },
          "heading": {
            "description": "Heading for dedicated servers.",
            "type": "string",
            "example": "Dedicated Servers"
          },
          "buy_link": {
            "description": "Link to order server.",
            "type": "string",
            "example": "order_server"
          },
          "list_link": {
            "description": "Link to view servers list.",
            "type": "string",
            "example": "view_servers_list"
          }
        }
      },
      "HomeDetailsModulesVps": {
        "description": "Dashboard module configuration for VPS services.",
        "type": "object",
        "properties": {
          "icon": {
            "description": "The icon for VPS.",
            "type": "string",
            "example": "cloud-meatball"
          },
          "view_link": {
            "description": "Link to view VPS.",
            "type": "string",
            "example": "view_vps"
          },
          "heading": {
            "description": "Heading for VPS.",
            "type": "string",
            "example": "VPS"
          },
          "buy_link": {
            "description": "Link to order VPS.",
            "type": "string",
            "example": "order_vps"
          },
          "list_link": {
            "description": "Link to view VPS list.",
            "type": "string",
            "example": "view_vps_list"
          }
        }
      },
      "HomeDetailsModulesWebhosting": {
        "description": "Dashboard module configuration for webhosting services.",
        "type": "object",
        "properties": {
          "icon": {
            "description": "The icon for web hosting.",
            "type": "string",
            "example": "window-maximize"
          },
          "view_link": {
            "description": "Link to view website.",
            "type": "string",
            "example": "view_website"
          },
          "heading": {
            "description": "Heading for web hosting.",
            "type": "string",
            "example": "Web Hosting"
          },
          "buy_link": {
            "description": "Link to order website.",
            "type": "string",
            "example": "order_website"
          },
          "list_link": {
            "description": "Link to view websites list.",
            "type": "string",
            "example": "view_websites_list"
          }
        }
      },
      "HomeServicesDomainsLinks": {
        "description": "Map of domain service IDs to their hostnames for the account dashboard.",
        "type": "object",
        "properties": {
          "376114": {
            "description": "Link to a domain.",
            "type": "string",
            "example": "pimpmy.website"
          },
          "376503": {
            "description": "Link to a domain.",
            "type": "string",
            "example": "hostingenuity.com"
          },
          "592337": {
            "description": "Link to a domain.",
            "type": "string",
            "example": "detain.dev"
          }
        }
      },
      "HomeServicesLicensesLinks": {
        "description": "Map of license service IDs to their IP addresses for the account dashboard.",
        "type": "object",
        "properties": {
          "386522": {
            "description": "Link to a license.",
            "type": "string",
            "example": "1.2.3.4"
          }
        }
      },
      "HomeServicesServersLinks": {
        "description": "Map of dedicated server service IDs to their hostnames for the account dashboard.",
        "type": "object",
        "properties": {
          "16058": {
            "description": "Link to a server.",
            "type": "string",
            "example": "anotherserver.com"
          }
        }
      },
      "HomeServicesVpsLinks": {
        "description": "Map of VPS service IDs to their hostnames for the account dashboard.",
        "type": "object",
        "properties": {
          "465503": {
            "description": "Link to a VPS.",
            "type": "string",
            "example": "vps465503"
          },
          "2500081": {
            "description": "Link to a VPS.",
            "type": "string",
            "example": "vps2500081"
          },
          "2578866": {
            "description": "Link to a VPS.",
            "type": "string",
            "example": "vps2578866"
          }
        }
      },
      "HomeServicesWebhostingLinks": {
        "description": "Map of webhosting service IDs to their hostnames for the account dashboard.",
        "type": "object",
        "properties": {
          "376359": {
            "description": "Link to a website.",
            "type": "string",
            "example": "Doefamily.com"
          },
          "376473": {
            "description": "Link to a website.",
            "type": "string",
            "example": "fancytush.com"
          },
          "386218": {
            "description": "Link to a website.",
            "type": "string",
            "example": "admincoded.net"
          }
        }
      },
      "HostnameObject": {
        "title": "HostnameObject",
        "description": "Request specifying the hostname.",
        "type": "object",
        "properties": {
          "hostname": {
            "type": "string"
          }
        },
        "example": {
          "hostname": "new.host.com"
        }
      },
      "IpLimitRange": {
        "title": "IpLimitRange",
        "description": "The lower and upper bounds of an ip range.",
        "required": [
          "start",
          "end"
        ],
        "type": "object",
        "properties": {
          "start": {
            "description": "The begining (or first) IP address in the range.",
            "type": "string",
            "example": "1.2.3.0"
          },
          "end": {
            "description": "The ending (or last) IP address in the range.",
            "type": "string",
            "example": "1.2.3.255"
          }
        },
        "example": {
          "start": "1.2.3.0",
          "end": "1.2.3.255"
        }
      },
      "License": {
        "required": [
          "serviceInfo",
          "client_links",
          "billingDetails",
          "custCurrency",
          "custCurrencySymbol",
          "package",
          "serviceExtra",
          "extraInfoTables",
          "service_overview_extra",
          "serviceType",
          "license_key"
        ],
        "type": "object",
        "properties": {
          "serviceInfo": {
            "$ref": "#/components/schemas/LicenseServiceInfo"
          },
          "client_links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LicenseClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/LicenseBillingDetails"
          },
          "custCurrency": {
            "description": "Customer's currency",
            "type": "string",
            "example": "USD"
          },
          "custCurrencySymbol": {
            "description": "Currency symbol for customer",
            "type": "string",
            "example": "$"
          },
          "package": {
            "description": "Package name",
            "type": "string",
            "example": "KernelCare License"
          },
          "serviceExtra": {
            "description": "Extra service information",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "extraInfoTables": {
            "type": "object",
            "properties": {
              "ip_info": {
                "$ref": "#/components/schemas/LicenseIpInfo"
              }
            }
          },
          "service_overview_extra": {
            "description": "Extra service overview information",
            "type": "string"
          },
          "serviceType": {
            "$ref": "#/components/schemas/LicenseServiceType"
          },
          "license_key": {
            "description": "License key",
            "type": "string",
            "example": ""
          }
        }
      },
      "LicenseBillingDetails": {
        "description": "Billing information for a software license including payment status, billing cycle, and cost.",
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "description": "Last invoice date",
            "type": "string",
            "example": "August 14, 2023"
          },
          "service_payment_status": {
            "description": "Payment status",
            "type": "string",
            "example": "Paid"
          },
          "service_frequency": {
            "description": "Service frequency",
            "type": "string",
            "example": "Monthly"
          },
          "next_date": {
            "format": "date-time",
            "description": "Next date",
            "type": "string",
            "example": "2023-09-14T09:39:46.000Z"
          },
          "service_next_invoice_date": {
            "description": "Next invoice date",
            "type": "string",
            "example": "September 14, 2023"
          },
          "service_currency": {
            "description": "Service currency",
            "type": "string",
            "example": "USD"
          },
          "service_currency_symbol": {
            "description": "Service currency symbol",
            "type": "string",
            "example": "$"
          },
          "service_coupon": {
            "description": "Service coupon",
            "type": "string",
            "example": "ACOUPONFORLICENSES"
          },
          "service_cost_info": {
            "description": "Service cost information",
            "type": "string",
            "example": "0.00"
          },
          "service_extra": {
            "description": "Additional service information",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "service_extra_json": {
            "description": "Additional service information in JSON format",
            "type": "string",
            "example": "[\"\"]"
          }
        }
      },
      "LicenseClientLink": {
        "required": [
          "label",
          "link",
          "icon",
          "help_text"
        ],
        "type": "object",
        "properties": {
          "label": {
            "description": "Link label",
            "type": "string",
            "example": "Invoices"
          },
          "link": {
            "description": "Link URL",
            "type": "string",
            "example": "invoices"
          },
          "icon": {
            "description": "Link icon",
            "type": "string",
            "example": "fas fa-file-invoice-dollar fa-w-12"
          },
          "icon_text": {
            "description": "Icon text",
            "type": "string",
            "example": ""
          },
          "help_text": {
            "description": "Help text",
            "type": "string",
            "example": "Invoice History"
          },
          "other_attr": {
            "description": "Other attributes",
            "type": "string",
            "example": ""
          }
        }
      },
      "LicenseIpInfo": {
        "description": "IP address information table for a software license service.",
        "type": "object",
        "properties": {
          "title": {
            "description": "Table title",
            "type": "string",
            "example": "IP Information"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LicenseIpInfoRow"
            }
          }
        }
      },
      "LicenseIpInfoRow": {
        "description": "A single row in the license IP information table.",
        "type": "object",
        "properties": {
          "desc": {
            "description": "Row description",
            "type": "string",
            "example": "Netmask"
          },
          "value": {
            "description": "Row value",
            "type": "string",
            "example": "255.255.255.248"
          }
        }
      },
      "LicenseRow": {
        "title": "LicenseRow",
        "description": "A result row from the `Licenses` `GET` request.",
        "type": "object",
        "properties": {
          "license_id": {
            "description": "The id of the license.",
            "type": "string",
            "example": "386111"
          },
          "license_hostname": {
            "description": "The hostname of the license.",
            "type": "string",
            "example": ""
          },
          "license_ip": {
            "description": "The ip of the license.",
            "type": "string",
            "example": "66.45.228.100"
          },
          "services_name": {
            "description": "The services name of the license.",
            "type": "string",
            "example": "Imunify360 up to 30 users"
          },
          "cost": {
            "description": "The cost of the license.",
            "type": "string",
            "example": "25.00"
          },
          "license_status": {
            "description": "The status of the license.",
            "type": "string",
            "example": "canceled"
          },
          "invoices_paid": {
            "description": "The invoices paid of the license.",
            "type": "string",
            "example": "1"
          },
          "invoices_date": {
            "format": "date-time",
            "description": "The invoices date of the license.",
            "type": "string",
            "example": "2019-08-28T14:27:22.000Z"
          }
        },
        "example": {
          "license_id": "386111",
          "license_hostname": "",
          "license_ip": "66.45.228.100",
          "services_name": "Imunify360 up to 30 users",
          "cost": "25.00",
          "license_status": "canceled",
          "invoices_paid": "1",
          "invoices_date": "2019-08-28T14:27:22.000Z"
        }
      },
      "LicenseServiceInfo": {
        "required": [
          "license_id",
          "license_type",
          "license_currency",
          "license_order_date",
          "license_custid",
          "license_ip",
          "license_status",
          "license_invoice",
          "license_coupon"
        ],
        "type": "object",
        "properties": {
          "license_id": {
            "description": "License ID",
            "type": "string",
            "example": "386522"
          },
          "license_type": {
            "description": "License type",
            "type": "string",
            "example": "5034"
          },
          "license_currency": {
            "description": "License currency",
            "type": "string",
            "example": "USD"
          },
          "license_order_date": {
            "format": "date-time",
            "description": "License order date",
            "type": "string",
            "example": "2020-01-14T10:48:14.000Z"
          },
          "license_custid": {
            "description": "Customer ID",
            "type": "string",
            "example": "771282"
          },
          "license_ip": {
            "description": "License IP",
            "type": "string",
            "example": "1.2.3.4"
          },
          "license_status": {
            "description": "License status",
            "type": "string",
            "example": "active"
          },
          "license_hostname": {
            "description": "License hostname",
            "type": "string",
            "example": ""
          },
          "license_key": {
            "description": "License key",
            "type": "string",
            "example": ""
          },
          "license_invoice": {
            "description": "License invoice",
            "type": "string",
            "example": "18704419"
          },
          "license_coupon": {
            "description": "License coupon",
            "type": "string",
            "example": "1836"
          },
          "license_extra": {
            "description": "Additional license information",
            "type": "string",
            "example": ""
          }
        }
      },
      "LicenseServiceType": {
        "description": "Service type definition for a software license, including name, pricing, and category.",
        "type": "object",
        "properties": {
          "services_id": {
            "description": "Service ID",
            "type": "string",
            "example": "5034"
          },
          "services_name": {
            "description": "Service name",
            "type": "string",
            "example": "KernelCare License"
          },
          "services_cost": {
            "description": "Service cost",
            "type": "string",
            "example": "2.95"
          },
          "services_category": {
            "description": "Service category",
            "type": "string",
            "example": "508"
          },
          "services_buyable": {
            "description": "Buyable status",
            "type": "string",
            "example": "1"
          },
          "services_type": {
            "description": "Service type",
            "type": "string",
            "example": "508"
          },
          "services_field1": {
            "description": "Service field 1",
            "type": "string",
            "example": "16"
          },
          "services_field2": {
            "description": "Service field 2",
            "type": "string",
            "example": ""
          },
          "services_module": {
            "description": "Service module",
            "type": "string",
            "example": "licenses"
          }
        }
      },
      "LicensesOrder": {
        "description": "Available license packages and pricing for ordering a new software license.",
        "type": "object",
        "properties": {
          "serviceCategories": {
            "description": "License service categories",
            "type": "object",
            "properties": {
              "LicensesOrderServiceCategories509": {
                "$ref": "#/components/schemas/LicensesOrderServiceCategories509"
              }
            }
          },
          "packageCosts": {
            "description": "Costs of license packages",
            "type": "object",
            "properties": {
              "LicensesOrderPackageCosts11468": {
                "description": "Cost of package with ID 11468",
                "type": "number",
                "example": 3.75
              }
            }
          },
          "serviceTypes": {
            "description": "Types of license services",
            "type": "object",
            "properties": {
              "LicensesOrderServiceTypes11482": {
                "$ref": "#/components/schemas/LicensesOrderServiceTypes11482"
              }
            }
          }
        }
      },
      "LicensesOrderServiceCategories509": {
        "type": "object",
        "properties": {
          "category_id": {
            "description": "Category ID",
            "type": "string",
            "example": "509"
          },
          "category_name": {
            "description": "Category Name",
            "type": "string",
            "example": "Webuzo"
          },
          "category_tag": {
            "description": "Category Tag",
            "type": "string",
            "example": "webuzo"
          },
          "category_module": {
            "description": "Category Module",
            "type": "string",
            "example": "licenses"
          }
        }
      },
      "LicensesOrderServiceTypes11482": {
        "type": "object",
        "properties": {
          "services_id": {
            "description": "Service ID",
            "type": "string",
            "example": "11482"
          },
          "services_name": {
            "description": "Service Name",
            "type": "string",
            "example": "DirectAdmin Standard"
          },
          "services_cost": {
            "description": "Service Cost",
            "type": "string",
            "example": "21.75"
          },
          "services_category": {
            "description": "Service Category",
            "type": "string",
            "example": "506"
          },
          "services_buyable": {
            "description": "Buyable flag for Service",
            "type": "string",
            "example": "1"
          },
          "services_type": {
            "description": "Service Type",
            "type": "string",
            "example": "506"
          },
          "services_field1": {
            "description": "Field 1 of Service",
            "type": "string",
            "example": "2704,Standard"
          },
          "services_field2": {
            "description": "Field 2 of Service",
            "type": "string",
            "example": "Unlimited Accounts & Domains<br><br>The DirectAdmin standard license is best for medium to large shared-hosting and/or reseller-hosting companies. No limit on accounts or domains."
          },
          "services_module": {
            "description": "Module of Service",
            "type": "string",
            "example": "licenses"
          }
        }
      },
      "LoginErrorResponse": {
        "title": "LoginErrorResponse",
        "description": "Error resposne during login indicating further action.",
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "field": {
            "type": "string"
          }
        },
        "example": {
          "message": "missing 2fa",
          "field": "2fa"
        }
      },
      "LoginInfo": {
        "title": "LoginInfo",
        "description": "Basic information useful for rendering a login page.",
        "required": [
          "captcha",
          "counts"
        ],
        "type": "object",
        "properties": {
          "logo": {
            "description": "A logo image url.",
            "type": "string",
            "example": "//my.interserver.net/images/logos/mystaging.png"
          },
          "captcha": {
            "description": "A base64 encoded image to use for rendering the alternateive captcha.",
            "type": "string",
            "example": "data:image/jpeg;base64,/9j/"
          },
          "language": {
            "description": "The desired langauge to render the site with.",
            "type": "string",
            "example": "en-US"
          },
          "counts": {
            "$ref": "#/components/schemas/LoginServiceCounts"
          }
        },
        "example": {
          "logo": "//my.interserver.net/images/logos/mystaging.png",
          "captcha": "data:image/jpeg;base64,/9j/",
          "language": "en-US",
          "counts": {
            "vps": 290201,
            "websites": 205172,
            "servers": 27940
          }
        }
      },
      "LoginServiceCounts": {
        "title": "LoginServiceCounts",
        "description": "Order counts per module.",
        "required": [
          "websites",
          "vps",
          "servers"
        ],
        "type": "object",
        "properties": {
          "vps": {
            "format": "int32",
            "description": "The number of total VPS orders that have been placed in our billing system.",
            "type": "integer",
            "example": 205172
          },
          "websites": {
            "format": "int32",
            "description": "The number of total website orders that have been placed in our billing system.",
            "type": "integer",
            "example": 205172
          },
          "servers": {
            "format": "int32",
            "description": "The number of total server orders that have been placed in our billing system.",
            "type": "integer",
            "example": 205172
          }
        },
        "example": {
          "vps": 290201,
          "websites": 205172,
          "servers": 27940
        }
      },
      "LoginSubmissionExample": {
        "title": "LoginSubmissionExample",
        "description": "The data to submit in the login request.",
        "required": [
          "login",
          "passwd"
        ],
        "type": "object",
        "properties": {
          "login": {
            "type": "string"
          },
          "passwd": {
            "type": "string"
          },
          "remember": {
            "type": "string"
          },
          "g-recaptcha-response": {
            "type": "object",
            "properties": {
              "__v_isShallow": {
                "type": "boolean"
              },
              "dep": {
                "type": "object",
                "properties": {
                  "w": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "n": {
                    "format": "int32",
                    "type": "integer"
                  }
                }
              },
              "__v_isRef": {
                "type": "boolean"
              },
              "_rawValue": {
                "type": "string"
              },
              "_value": {
                "type": "string"
              }
            }
          },
          "tfa": {
            "description": "Two Factor Authentication Response.",
            "type": "string"
          }
        },
        "example": {
          "login": "user@domain.com",
          "passwd": "mypassword",
          "remember": "true",
          "g-recaptcha-response": {
            "__v_isShallow": false,
            "dep": {
              "w": 0,
              "n": 0
            },
            "__v_isRef": true,
            "_rawValue": "zzzzz",
            "_value": "zzzzz"
          }
        }
      },
      "MailBillingDetails": {
        "description": "Billing information for a mail service including payment status, billing cycle, and cost.",
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "description": "The last invoice date of the service.",
            "type": "string",
            "example": "July 16, 2023"
          },
          "service_payment_status": {
            "description": "The payment status of the service.",
            "type": "string",
            "example": "Unpaid"
          },
          "service_frequency": {
            "description": "The frequency of the service payment.",
            "type": "string",
            "example": "Monthly"
          },
          "next_date": {
            "description": "The next payment date of the service.",
            "type": "string",
            "example": "2023-08-16T00:55:05.000Z"
          },
          "service_next_invoice_date": {
            "description": "The next invoice date of the service.",
            "type": "string",
            "example": "August 16, 2023"
          },
          "service_currency": {
            "description": "The currency of the service.",
            "type": "string",
            "example": "USD"
          },
          "service_currency_symbol": {
            "description": "The currency symbol of the service.",
            "type": "string",
            "example": "$"
          },
          "service_cost_info": {
            "description": "The cost information of the service.",
            "type": "string",
            "example": "1.00"
          },
          "service_extra": {
            "description": "Extra information for the service.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "service_extra_json": {
            "description": "Extra JSON information for the service.",
            "type": "string",
            "example": "[]"
          }
        }
      },
      "MailClientLink": {
        "description": "A navigation link for mail service actions in the client portal.",
        "type": "object",
        "properties": {
          "label": {
            "description": "The label of the client link.",
            "type": "string",
            "example": "Invoices"
          },
          "link": {
            "description": "The link URL of the client link.",
            "type": "string",
            "example": "invoices"
          },
          "icon": {
            "description": "The icon class of the client link.",
            "type": "string",
            "example": "fas fa-file-invoice-dollar fa-w-12"
          },
          "icon_text": {
            "description": "The text for the icon of the client link.",
            "type": "string",
            "example": ""
          },
          "help_text": {
            "description": "Help text for the client link.",
            "type": "string",
            "example": "Invoice History"
          },
          "other_attr": {
            "description": "Additional attributes for the client link.",
            "type": "string",
            "example": ""
          }
        }
      },
      "MailExtraInfoTable": {
        "description": "A supplementary information table for a mail service (e.g., connection details).",
        "type": "object",
        "properties": {
          "title": {
            "description": "The title of the extra info table.",
            "type": "string",
            "example": "Connection Information"
          },
          "rows": {
            "description": "The rows of the extra info table.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailExtraInfoTableRow"
            }
          }
        }
      },
      "MailExtraInfoTableRow": {
        "description": "A single row in the mail service supplementary information table.",
        "type": "object",
        "properties": {
          "desc": {
            "description": "The description of the extra info table row.",
            "type": "string",
            "example": "SMTP Server"
          },
          "value": {
            "description": "The value of the extra info table row.",
            "type": "string",
            "example": "relay.mailbaby.net"
          }
        }
      },
      "MailOrder": {
        "title": "MailOrder",
        "description": "A mail order record",
        "required": [
          "status",
          "id",
          "username"
        ],
        "type": "object",
        "properties": {
          "id": {
            "format": "int32",
            "description": "The ID of the order.",
            "type": "integer",
            "example": 21472
          },
          "status": {
            "description": "The order status.",
            "type": "string",
            "example": "active"
          },
          "username": {
            "description": "The username to use for this order.",
            "type": "string",
            "example": "mb21472"
          },
          "comment": {
            "description": "Optional order comment.",
            "type": "string"
          }
        },
        "example": {
          "id": 21472,
          "status": "active",
          "username": "mb21472"
        }
      },
      "MailRow": {
        "title": "MailRow",
        "description": "A result row from the `Mail` `GET` request.",
        "type": "object",
        "properties": {
          "mail_id": {
            "description": "The id of the mail.",
            "type": "string",
            "example": "5652"
          },
          "repeat_invoices_cost": {
            "description": "The repeat invoices cost of the mail.",
            "type": "string",
            "example": "5.99"
          },
          "mail_username": {
            "description": "The username of the mail.",
            "type": "string",
            "example": "mb5652"
          },
          "mail_status": {
            "description": "The status of the mail.",
            "type": "string",
            "example": "expired"
          },
          "services_name": {
            "description": "The services name of the mail.",
            "type": "string",
            "example": "MailBaby Mail"
          }
        },
        "example": {
          "mail_id": "5652",
          "repeat_invoices_cost": "5.99",
          "mail_username": "mb5652",
          "mail_status": "expired",
          "services_name": "MailBaby Mail"
        }
      },
      "MailSchema": {
        "required": [
          "serviceInfo",
          "client_links",
          "billingDetails",
          "custCurrency",
          "custCurrencySymbol",
          "package",
          "extraInfoTables",
          "serviceType",
          "usage_count"
        ],
        "type": "object",
        "properties": {
          "serviceInfo": {
            "$ref": "#/components/schemas/MailServiceInfo"
          },
          "client_links": {
            "description": "Links related to the mail service for clients.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/MailBillingDetails"
          },
          "custCurrency": {
            "description": "The customer's currency.",
            "type": "string",
            "example": "USD"
          },
          "custCurrencySymbol": {
            "description": "The currency symbol for the customer.",
            "type": "string",
            "example": "$"
          },
          "package": {
            "description": "The package of the mail service.",
            "type": "string",
            "example": "MailBaby Mail"
          },
          "serviceExtra": {
            "description": "Extra information for the mail service.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "extraInfoTables": {
            "description": "Additional information tables for the mail service.",
            "type": "object",
            "properties": {
              "mail": {
                "$ref": "#/components/schemas/MailExtraInfoTable"
              },
              "tutorials": {
                "$ref": "#/components/schemas/MailTutorialsTable"
              }
            }
          },
          "serviceType": {
            "$ref": "#/components/schemas/MailServiceType"
          },
          "usage_count": {
            "description": "The usage count of the mail service.",
            "type": "string",
            "example": "0"
          }
        }
      },
      "MailAlertsResponse": {
        "description": "Alert configuration entries for a mail service.",
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "alert_id": {
              "type": "integer"
            },
            "alert_type": {
              "type": "string"
            },
            "alert_value": {
              "type": "string"
            },
            "alert_to": {
              "type": "string"
            },
            "alert_enabled": {
              "type": "string"
            }
          }
        }
      },
      "MailAlertRequest": {
        "description": "Payload for creating a mail alert.",
        "type": "object",
        "properties": {
          "type": {
            "description": "Alert type identifier.",
            "type": "string"
          },
          "value": {
            "description": "Alert value or threshold.",
            "type": "string"
          },
          "to": {
            "description": "Email address to notify.",
            "type": "string"
          },
          "enabled": {
            "description": "Whether the alert is enabled.",
            "type": "string"
          }
        }
      },
      "MailAlertUpdateRequest": {
        "description": "Payload for updating an existing mail alert.",
        "type": "object",
        "properties": {
          "alert_id": {
            "description": "Alert ID to update.",
            "type": "integer"
          },
          "type": {
            "description": "Alert type identifier.",
            "type": "string"
          },
          "value": {
            "description": "Alert value or threshold.",
            "type": "string"
          },
          "to": {
            "description": "Email address to notify.",
            "type": "string"
          },
          "enabled": {
            "description": "Whether the alert is enabled.",
            "type": "string"
          }
        }
      },
      "MailDelistRequest": {
        "description": "Payload for removing a sender from mail blocklists.",
        "type": "object",
        "properties": {
          "unblock": {
            "description": "Email address to delist.",
            "type": "string"
          }
        }
      },
      "MailDelistResponse": {
        "description": "Blocklist status information for a mail service.",
        "type": "object",
        "properties": {
          "id": {
            "description": "Mail service ID.",
            "type": "integer"
          },
          "local": {
            "description": "Local blocklist entries.",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "mbtrap": {
            "description": "MailBaby trap block entries.",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "subject": {
            "description": "Subject-based block entries.",
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "manual": {
            "description": "Manually blocked entries.",
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "MailDeliverabilityResponse": {
        "description": "Deliverability statistics for a mail service.",
        "type": "object",
        "properties": {
          "stat": {
            "description": "Delivered and bounced counts.",
            "type": "object"
          },
          "percent": {
            "description": "Bounce percentage.",
            "type": "number"
          },
          "table_data": {
            "description": "Detailed deliverability breakdown by sender or domain.",
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "MailServiceInfo": {
        "required": [
          "mail_id",
          "mail_type",
          "mail_currency",
          "mail_order_date",
          "mail_custid",
          "mail_quota",
          "mail_status",
          "mail_invoice"
        ],
        "type": "object",
        "properties": {
          "mail_id": {
            "description": "The ID of the mail service.",
            "type": "string",
            "example": "43171"
          },
          "mail_username": {
            "description": "The username associated with the mail service.",
            "type": "string",
            "example": ""
          },
          "mail_type": {
            "description": "The type of mail service.",
            "type": "string",
            "example": "10880"
          },
          "mail_currency": {
            "description": "The currency of the mail service.",
            "type": "string",
            "example": "USD"
          },
          "mail_order_date": {
            "description": "The order date of the mail service.",
            "type": "string",
            "example": "2023-07-16T00:55:05.000Z"
          },
          "mail_custid": {
            "description": "The customer ID associated with the mail service.",
            "type": "string",
            "example": "771282"
          },
          "mail_quota": {
            "description": "The mail quota for the service.",
            "type": "string",
            "example": "0"
          },
          "mail_ip": {
            "description": "The IP address associated with the mail service.",
            "type": "string",
            "example": ""
          },
          "mail_status": {
            "description": "The status of the mail service.",
            "type": "string",
            "example": "expired"
          },
          "mail_invoice": {
            "description": "The invoice ID of the mail service.",
            "type": "string",
            "example": "20410322"
          },
          "mail_coupon": {
            "description": "The coupon associated with the mail service.",
            "type": "string",
            "example": "0"
          },
          "mail_extra": {
            "description": "Additional information for the mail service.",
            "type": "string",
            "example": "[]"
          },
          "mail_server_status": {
            "description": "The server status of the mail service.",
            "type": "string",
            "example": ""
          },
          "mail_comment": {
            "description": "Additional comments for the mail service.",
            "type": "string",
            "example": ""
          }
        }
      },
      "MailServiceType": {
        "description": "Service type definition for a mail service, including name, pricing, and category.",
        "type": "object",
        "properties": {
          "services_id": {
            "description": "The ID of the service type.",
            "type": "string",
            "example": "10880"
          },
          "services_name": {
            "description": "The name of the service type.",
            "type": "string",
            "example": "MailBaby Mail"
          },
          "services_cost": {
            "description": "The cost of the service type.",
            "type": "string",
            "example": "1.00"
          },
          "services_category": {
            "description": "The category of the service type.",
            "type": "string",
            "example": "800"
          },
          "services_buyable": {
            "description": "Whether the service type is buyable.",
            "type": "string",
            "example": "1"
          },
          "services_type": {
            "description": "The type of the service type.",
            "type": "string",
            "example": "800"
          },
          "services_field1": {
            "description": "Additional field for the service type.",
            "type": "string",
            "example": ""
          },
          "services_field2": {
            "description": "Additional field for the service type.",
            "type": "string",
            "example": ""
          },
          "services_module": {
            "description": "The module of the service type.",
            "type": "string",
            "example": "mail"
          }
        }
      },
      "MailTutorialsTable": {
        "description": "A table of tutorial links displayed for a mail service.",
        "type": "object",
        "properties": {
          "title": {
            "description": "The title of the tutorials table.",
            "type": "string",
            "example": "Tutorials"
          },
          "rows": {
            "description": "The rows of the tutorials table.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailTutorialsTableRow"
            }
          }
        }
      },
      "MailTutorialsTableRow": {
        "description": "A single tutorial entry with a label and URL.",
        "type": "object",
        "properties": {
          "desc": {
            "description": "The description of the tutorials table row.",
            "type": "string",
            "example": "cPanel Tutorial"
          },
          "value": {
            "description": "The value of the tutorials table row.",
            "type": "string",
            "example": "<a class=\"link\" href=\"https://mail.baby/cpanel/\" target=\"_blank\">Click Here</a>"
          }
        }
      },
      "ModuleSettings": {
        "title": "ModuleSettings",
        "description": "The settings for a module.",
        "required": [
          "USE_REPEAT_INVOICE",
          "BILLING_DAYS_OFFSET",
          "DELETE_PENDING_DAYS",
          "EMAIL_FROM",
          "IMGNAME",
          "MENUNAME",
          "TBLNAME",
          "TABLE",
          "SUSPEND_WARNING_DAYS",
          "SUSPEND_DAYS",
          "SERVICE_ID_OFFSET",
          "REPEAT_BILLING_METHOD",
          "PREFIX",
          "TITLE",
          "TITLE_FIELD",
          "USE_PACKAGES"
        ],
        "type": "object",
        "properties": {
          "SERVICE_ID_OFFSET": {
            "format": "int32",
            "type": "integer",
            "example": 0
          },
          "USE_REPEAT_INVOICE": {
            "type": "boolean",
            "example": true
          },
          "USE_PACKAGES": {
            "type": "boolean",
            "example": true
          },
          "BILLING_DAYS_OFFSET": {
            "format": "int32",
            "type": "integer",
            "example": 0
          },
          "IMGNAME": {
            "type": "string",
            "example": "root-server.png"
          },
          "REPEAT_BILLING_METHOD": {
            "format": "int32",
            "type": "integer",
            "example": 2
          },
          "DELETE_PENDING_DAYS": {
            "format": "int32",
            "type": "integer",
            "example": 45
          },
          "SUSPEND_DAYS": {
            "format": "int32",
            "type": "integer",
            "example": 14
          },
          "SUSPEND_WARNING_DAYS": {
            "format": "int32",
            "type": "integer",
            "example": 7
          },
          "TITLE": {
            "type": "string",
            "example": "VPS"
          },
          "MENUNAME": {
            "type": "string",
            "example": "VPS"
          },
          "EMAIL_FROM": {
            "type": "string",
            "example": "support@interserver.net\""
          },
          "TBLNAME": {
            "type": "string",
            "example": "VPS"
          },
          "TABLE": {
            "type": "string",
            "example": "vps"
          },
          "TITLE_FIELD": {
            "type": "string",
            "example": "vps_hostname"
          },
          "TITLE_FIELD2": {
            "type": "string",
            "example": "vps_ip"
          },
          "TITLE_FIELD3": {
            "type": "string",
            "example": "vps_vzid"
          },
          "PREFIX": {
            "type": "string",
            "example": "vps"
          }
        },
        "example": {
          "SERVICE_ID_OFFSET": 0,
          "USE_REPEAT_INVOICE": true,
          "USE_PACKAGES": true,
          "BILLING_DAYS_OFFSET": 0,
          "IMGNAME": "root-server.png",
          "REPEAT_BILLING_METHOD": 2,
          "DELETE_PENDING_DAYS": 45,
          "SUSPEND_DAYS": 14,
          "SUSPEND_WARNING_DAYS": 7,
          "TITLE": "VPS",
          "MENUNAME": "VPS",
          "EMAIL_FROM": "support@interserver.net",
          "TBLNAME": "VPS",
          "TABLE": "vps",
          "TITLE_FIELD": "vps_hostname",
          "TITLE_FIELD2": "vps_ip",
          "TITLE_FIELD3": "vps_vzid",
          "PREFIX": "vps"
        }
      },
      "Modules": {
        "description": "The modules and settings.",
        "type": "object",
        "additionalProperties": {
          "$ref": "#/components/schemas/ModuleSettings"
        },
        "example": {
          "domains": {
            "SERVICE_ID_OFFSET": 10000,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 45,
            "IMGNAME": "domain.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Domain Registrations",
            "MENUNAME": "Domains",
            "EMAIL_FROM": "support@interserver.net",
            "TBLNAME": "Domains",
            "TABLE": "domains",
            "TITLE_FIELD": "domain_hostname",
            "PREFIX": "domain"
          },
          "vps": {
            "SERVICE_ID_OFFSET": 0,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "root-server.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "VPS",
            "MENUNAME": "VPS",
            "EMAIL_FROM": "support@interserver.net",
            "TBLNAME": "VPS",
            "TABLE": "vps",
            "TITLE_FIELD": "vps_hostname",
            "TITLE_FIELD2": "vps_ip",
            "TITLE_FIELD3": "vps_vzid",
            "PREFIX": "vps"
          },
          "backups": {
            "SERVICE_ID_OFFSET": 2000,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "network-drive.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Backup Services",
            "MENUNAME": "Storage",
            "EMAIL_FROM": "support@interserver.net",
            "TBLNAME": "Storage",
            "TABLE": "backups",
            "TITLE_FIELD": "backup_username",
            "TITLE_FIELD2": "backup_ip",
            "PREFIX": "backup"
          },
          "mail": {
            "SERVICE_ID_OFFSET": 11000,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "e-mail.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Mail Services",
            "MENUNAME": "Mail",
            "EMAIL_FROM": "support@interserver.net",
            "TBLNAME": "Mail",
            "TABLE": "mail",
            "TITLE_FIELD": "mail_username",
            "TITLE_FIELD2": "mail_ip",
            "PREFIX": "mail"
          },
          "licenses": {
            "SERVICE_ID_OFFSET": 5000,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "certificate.png",
            "REPEAT_BILLING_METHOD": 1,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 9,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Licensing",
            "EMAIL_FROM": "invoice@cpaneldirect.net",
            "TBLNAME": "Licenses",
            "TABLE": "licenses",
            "PREFIX": "license",
            "TITLE_FIELD": "license_ip",
            "TITLE_FIELD2": "license_key",
            "TITLE_FIELD3": "license_hostname",
            "MENUNAME": "Licensing"
          },
          "ssl": {
            "SERVICE_ID_OFFSET": 3000,
            "USE_REPEAT_INVOICE": false,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "security-ssl.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "SSL Certificates",
            "MENUNAME": "SSL",
            "EMAIL_FROM": "support@inssl.net",
            "TBLNAME": "SSL",
            "TABLE": "ssl_certs",
            "TITLE_FIELD": "ssl_order_id",
            "PREFIX": "ssl"
          },
          "floating_ips": {
            "SERVICE_ID_OFFSET": 12000,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "e-mail.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Floating IPs",
            "MENUNAME": "Floating IPs",
            "EMAIL_FROM": "support@interserver.net",
            "TBLNAME": "Floating IPs",
            "TABLE": "floating_ips",
            "TITLE_FIELD": "floating_ip_ip",
            "TITLE_FIELD2": "floating_ip_target_ip",
            "PREFIX": "floating_ip"
          },
          "webhosting": {
            "SERVICE_ID_OFFSET": 1000,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": true,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "website.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Web Hosting",
            "EMAIL_FROM": "invoice@interserver.net",
            "TBLNAME": "Websites",
            "TABLE": "websites",
            "PREFIX": "website",
            "TITLE_FIELD": "website_hostname",
            "TITLE_FIELD2": "website_username",
            "TITLE_FIELD3": "website_ip",
            "MENUNAME": "Web Hosting"
          },
          "quickservers": {
            "SERVICE_ID_OFFSET": 0,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": false,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "server.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 30,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Rapid Deploy Servers",
            "MENUNAME": "Rapid Deploy Servers",
            "EMAIL_FROM": "support@interserver.net",
            "TBLNAME": "Rapid Deploy Servers",
            "TABLE": "quickservers",
            "TITLE_FIELD": "qs_hostname",
            "TITLE_FIELD2": "qs_ip",
            "TITLE_FIELD3": "qs_vzid",
            "PREFIX": "qs"
          },
          "servers": {
            "SERVICE_ID_OFFSET": 4000,
            "USE_REPEAT_INVOICE": true,
            "USE_PACKAGES": false,
            "BILLING_DAYS_OFFSET": 0,
            "IMGNAME": "stack.png",
            "REPEAT_BILLING_METHOD": 2,
            "DELETE_PENDING_DAYS": 45,
            "SUSPEND_DAYS": 14,
            "SUSPEND_WARNING_DAYS": 7,
            "TITLE": "Dedicated Servers",
            "MENUNAME": "Servers",
            "EMAIL_FROM": "support@interserver.net",
            "TBLNAME": "Servers",
            "TABLE": "servers",
            "TITLE_FIELD": "server_hostname",
            "PREFIX": "server"
          }
        }
      },
      "PasswordRequest": {
        "title": "PasswordRequest",
        "description": "Request containing a password",
        "required": [
          "password"
        ],
        "type": "object",
        "properties": {
          "password": {
            "type": "string"
          }
        },
        "example": {
          "password": "abcdefg123456"
        }
      },
      "QueueResponse": {
        "title": "QueueResponse",
        "description": "Response after sending an action queue to a service.",
        "required": [
          "queueId",
          "text"
        ],
        "type": "object",
        "properties": {
          "text": {
            "description": "Response text",
            "type": "string",
            "example": "Action has been sent to the server. Please allow up to 2 minutes for action to be completed."
          },
          "queueId": {
            "format": "int32",
            "description": "The Queue ID attached to the action.",
            "type": "integer",
            "example": 14670065
          }
        },
        "example": {
          "text": "Action has been sent to the server. Please allow up to 2 minutes for action to be completed.",
          "queueId": 14670065
        }
      },
      "Quickserver": {
        "required": [
          "serviceInfo",
          "client_links",
          "billingDetails",
          "custCurrency",
          "custCurrencySymbol",
          "serviceMaster",
          "package",
          "os_template",
          "serviceExtra",
          "extraInfoTables",
          "cpu_graph_data",
          "bandwidth_xaxis",
          "bandwidth_yaxis",
          "module",
          "token",
          "service_disk_used",
          "service_disk_total",
          "disk_percentage",
          "memory",
          "hdd",
          "service_overview_extra"
        ],
        "type": "object",
        "properties": {
          "serviceInfo": {
            "$ref": "#/components/schemas/QuickserverServiceInfo"
          },
          "client_links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuickserverClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/QuickserverBillingDetails"
          },
          "custCurrency": {
            "description": "Currency of the customer",
            "type": "string",
            "example": "USD"
          },
          "custCurrencySymbol": {
            "description": "Currency symbol of the customer",
            "type": "string",
            "example": "$"
          },
          "serviceMaster": {
            "$ref": "#/components/schemas/QuickserverServiceMaster"
          },
          "package": {
            "description": "Package name",
            "type": "string",
            "example": "Rapid Deploy Server"
          },
          "os_template": {
            "description": "Operating system template",
            "type": "string",
            "example": "Ubuntu 22.04"
          },
          "serviceExtra": {
            "type": "object",
            "properties": {
              "platform": {
                "description": "Platform information",
                "type": "string",
                "example": "kvm"
              }
            }
          },
          "extraInfoTables": {
            "type": "object",
            "properties": {
              "ip_info": {
                "$ref": "#/components/schemas/QuickserverIpInfo"
              },
              "addons": {
                "$ref": "#/components/schemas/QuickserverAddons"
              }
            }
          },
          "cpu_graph_data": {
            "description": "CPU graph data",
            "type": "string",
            "example": "{\"labels\":[],\"value\":[]}"
          },
          "bandwidth_xaxis": {
            "description": "Bandwidth x-axis data",
            "type": "string",
            "example": "[]"
          },
          "bandwidth_yaxis": {
            "description": "Bandwidth y-axis data",
            "type": "string",
            "example": "[]"
          },
          "module": {
            "description": "Module information",
            "type": "string",
            "example": "quickservers"
          },
          "token": {
            "description": "Authentication token",
            "type": "string",
            "example": "%3Ftoken%3DAKLFIJOAQIRFOPIEWQRURQOIURWQOI"
          },
          "service_disk_used": {
            "description": "Used disk space",
            "type": "string",
            "example": "0.00 GB"
          },
          "service_disk_total": {
            "description": "Total disk space",
            "type": "string",
            "example": "0.00 GB"
          },
          "disk_percentage": {
            "description": "Disk usage percentage",
            "type": "number",
            "example": 32.27
          },
          "memory": {
            "description": "Memory information",
            "type": "string",
            "example": "0GB"
          },
          "hdd": {
            "description": "HDD information",
            "type": "string",
            "example": "0GB"
          },
          "service_overview_extra": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "QuickserverAddons": {
        "type": "object",
        "properties": {
          "title": {
            "description": "Table title",
            "type": "string",
            "example": "Addons"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuickserverAddonsRow"
            }
          }
        }
      },
      "QuickserverAddonsRow": {
        "description": "A single add-on item associated with a QuickServer service.",
        "type": "object",
        "properties": {
          "desc": {
            "description": "Description",
            "type": "string",
            "example": "Additional IP Address"
          },
          "value": {
            "description": "Value",
            "type": "string",
            "example": ""
          }
        }
      },
      "QuickserverBillingDetails": {
        "description": "Billing information for a QuickServer service including payment status, billing cycle, and cost.",
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "description": "Last invoice date",
            "type": "string",
            "example": "April 11, 2023"
          },
          "service_payment_status": {
            "description": "Payment status",
            "type": "string",
            "example": "Paid"
          },
          "service_frequency": {
            "description": "Service frequency",
            "type": "string",
            "example": "Monthly"
          },
          "next_date": {
            "description": "Next date",
            "type": "string",
            "example": "2023-05-11T20:00:06.000Z"
          },
          "service_next_invoice_date": {
            "description": "Next invoice date",
            "type": "string",
            "example": "May 11, 2023"
          },
          "service_currency": {
            "description": "Currency",
            "type": "string",
            "example": "USD"
          },
          "service_currency_symbol": {
            "description": "Currency symbol",
            "type": "string",
            "example": "$"
          },
          "service_cost_info": {
            "description": "Cost information",
            "type": "string",
            "example": "49.00"
          },
          "service_extra": {
            "$ref": "#/components/schemas/QuickserverServiceExtra"
          },
          "service_extra_json": {
            "description": "Extra information (JSON format)",
            "type": "string",
            "example": "{\"platform\":\"kvm\"}"
          }
        }
      },
      "QuickserverClientLink": {
        "description": "A navigation link for QuickServer-related actions in the client portal.",
        "type": "object",
        "properties": {
          "label": {
            "description": "Link label",
            "type": "string",
            "example": "Invoices"
          },
          "link": {
            "description": "Link",
            "type": "string",
            "example": "invoices"
          },
          "icon": {
            "description": "Icon class",
            "type": "string",
            "example": "fas fa-file-invoice-dollar fa-w-12"
          },
          "icon_text": {
            "description": "Icon text",
            "type": "string",
            "example": ""
          },
          "help_text": {
            "description": "Help text",
            "type": "string",
            "example": "Invoice History"
          },
          "other_attr": {
            "description": "Other attribute",
            "type": "string"
          }
        }
      },
      "QuickserverIpInfo": {
        "description": "IP address information table for a QuickServer service.",
        "type": "object",
        "properties": {
          "title": {
            "description": "Table title",
            "type": "string",
            "example": "IP Information"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuickserverIpTableRow"
            }
          }
        }
      },
      "QuickserverIpTableRow": {
        "description": "A single row in the QuickServer IP information table.",
        "type": "object",
        "properties": {
          "desc": {
            "description": "Description",
            "type": "string",
            "example": "Netmask"
          },
          "value": {
            "description": "Value",
            "type": "string",
            "example": ""
          }
        }
      },
      "QuickserverOrder": {
        "description": "Available QuickServer options and OS templates for ordering a new QuickServer.",
        "type": "object",
        "properties": {
          "qs_id": {
            "description": "Quickserver ID.",
            "type": "string",
            "example": "221"
          },
          "server_details": {
            "description": "Server details.",
            "type": "object",
            "properties": {
              "381": {
                "$ref": "#/components/schemas/QuickserverOrderServerDetails381"
              }
            }
          },
          "templates": {
            "description": "Templates details.",
            "type": "object",
            "properties": {
              "Ubuntu": {
                "$ref": "#/components/schemas/QuickserverOrderTemplatesUbuntu64"
              }
            }
          },
          "version": {
            "description": "Version details.",
            "type": "object",
            "properties": {
              "centosstream-8": {
                "$ref": "#/components/schemas/QuickserverOrderVersionCentosstream8"
              }
            }
          },
          "distro_sel": {
            "description": "Distribution selection.",
            "type": "object",
            "properties": {
              "Ubuntu": {
                "$ref": "#/components/schemas/QuickserverOrderDistroSelUbuntu"
              }
            }
          }
        }
      },
      "QuickserverOrderDistroSelUbuntu": {
        "type": "object",
        "properties": {
          "Ubuntu": {
            "description": "Selected distribution for Ubuntu.",
            "type": "string",
            "example": "Ubuntu"
          }
        }
      },
      "QuickserverOrderServerDetails381": {
        "type": "object",
        "properties": {
          "cpu": {
            "description": "CPU details of the server.",
            "type": "string",
            "example": "AMD Ryzen 9 5900X 12-Core Processor"
          },
          "ram": {
            "description": "RAM capacity of the server.",
            "type": "string",
            "example": "119GB"
          },
          "hd": {
            "description": "Hard disk capacity of the server.",
            "type": "string",
            "example": "1760GB"
          },
          "cores": {
            "description": "Number of CPU cores.",
            "type": "integer",
            "example": 24
          },
          "cost": {
            "description": "Cost of the server.",
            "type": "string",
            "example": "$140.00"
          }
        }
      },
      "QuickserverOrderTemplatesUbuntu64": {
        "description": "Ubuntu template details.",
        "type": "array",
        "items": {
          "type": "array",
          "items": {
            "description": "Name of the Ubuntu template.",
            "type": "string",
            "example": "ubuntu24"
          }
        }
      },
      "QuickserverOrderVersionCentosstream8": {
        "type": "object",
        "properties": {
          "centosstream-8": {
            "description": "Version details of CentOS Stream 8.",
            "type": "string",
            "example": "8 Stream (64 bits)"
          }
        }
      },
      "QuickserverRow": {
        "title": "QuickserverRow",
        "description": "A result row from the `Quickservers` `GET` request.",
        "required": [
          "qs_status",
          "qs_id",
          "qs_comment",
          "cost",
          "qs_hostname",
          "qs_name"
        ],
        "type": "object",
        "properties": {
          "qs_id": {
            "description": "The id of the qs.",
            "type": "string",
            "example": "19504"
          },
          "qs_name": {
            "description": "The name of the qs.",
            "type": "string",
            "example": "Quickserver199"
          },
          "cost": {
            "description": "The cost of the qs.",
            "type": "string",
            "example": "65.00"
          },
          "qs_hostname": {
            "description": "The hostname of the qs.",
            "type": "string",
            "example": "qs19504"
          },
          "qs_status": {
            "description": "The status of the qs.",
            "type": "string",
            "example": "canceled"
          },
          "qs_comment": {
            "description": "The comment of the qs.",
            "type": "string",
            "example": ""
          }
        },
        "example": {
          "qs_id": "19504",
          "qs_name": "Quickserver199",
          "cost": "65.00",
          "qs_hostname": "qs19504",
          "qs_status": "canceled",
          "qs_comment": ""
        }
      },
      "QuickserverServiceExtra": {
        "type": "object",
        "properties": {
          "platform": {
            "description": "Platform information",
            "type": "string",
            "example": "kvm"
          }
        }
      },
      "QuickserverServiceInfo": {
        "description": "Core service record for a QuickServer including ID, status, IP, OS, and billing details.",
        "type": "object",
        "properties": {
          "qs_id": {
            "description": "Quickserver ID",
            "type": "string",
            "example": "24355"
          },
          "qs_custid": {
            "description": "Customer ID",
            "type": "string",
            "example": "771282"
          },
          "qs_server": {
            "description": "Server information",
            "type": "string",
            "example": "365"
          },
          "qs_ip": {
            "description": "IP address",
            "type": "string",
            "example": ""
          },
          "qs_ipv6": {
            "description": "IPv6 address (null)",
            "type": "string",
            "nullable": true
          },
          "qs_vzid": {
            "description": "VZ ID",
            "type": "string",
            "example": "qs24355"
          },
          "qs_currency": {
            "description": "Currency",
            "type": "string",
            "example": "USD"
          },
          "qs_type": {
            "description": "Type",
            "type": "string",
            "example": "700"
          },
          "qs_order_date": {
            "description": "Order date",
            "type": "string",
            "example": "2023-04-11T20:00:06.000Z"
          },
          "qs_status": {
            "description": "Status",
            "type": "string",
            "example": "canceled"
          },
          "qs_invoice": {
            "description": "Invoice number",
            "type": "string",
            "example": "20297531"
          },
          "qs_coupon": {
            "description": "Coupon information",
            "type": "string",
            "example": "0"
          },
          "qs_extra": {
            "description": "Extra information",
            "type": "string",
            "example": "{\"platform\":\"kvm\"}"
          },
          "qs_hostname": {
            "description": "Hostname",
            "type": "string",
            "example": "qs24355"
          },
          "qs_server_status": {
            "description": "Server status",
            "type": "string",
            "example": "deleted"
          },
          "qs_comment": {
            "description": "Comment",
            "type": "string",
            "example": ""
          },
          "qs_slices": {
            "description": "Slices information",
            "type": "string",
            "example": "0"
          },
          "qs_vnc": {
            "description": "VNC information",
            "type": "string",
            "example": "99.88.77.66"
          },
          "qs_vnc_port": {
            "description": "VNC port (null)",
            "type": "integer",
            "nullable": true
          },
          "qs_rootpass": {
            "description": "Root password",
            "type": "string",
            "example": ""
          },
          "qs_mac": {
            "description": "MAC address",
            "type": "string",
            "example": ""
          },
          "qs_os": {
            "description": "Operating system",
            "type": "string",
            "example": "ubuntu24"
          },
          "qs_version": {
            "description": "OS version",
            "type": "string",
            "example": "Ubuntu"
          },
          "qs_location": {
            "description": "Location",
            "type": "string",
            "example": "1"
          },
          "qs_platform": {
            "description": "Platform (null)",
            "type": "string",
            "nullable": true
          }
        }
      },
      "QuickserverServiceMaster": {
        "description": "Information about the host node running this QuickServer, including hardware specs and resource utilization.",
        "type": "object",
        "properties": {
          "qs_id": {
            "description": "Quickserver ID",
            "type": "string",
            "example": "365"
          },
          "qs_name": {
            "description": "Quickserver name",
            "type": "string",
            "example": "Qs365"
          },
          "qs_ip": {
            "description": "IP address",
            "type": "string",
            "example": ""
          },
          "qs_type": {
            "description": "Type",
            "type": "string",
            "example": "14"
          },
          "qs_hdsize": {
            "description": "HDD size",
            "type": "string",
            "example": "1760"
          },
          "qs_hdfree": {
            "description": "Free HDD space",
            "type": "string",
            "example": "1192"
          },
          "qs_bits": {
            "description": "Bits",
            "type": "string",
            "example": "64"
          },
          "qs_load": {
            "description": "Load",
            "type": "string",
            "example": "3.45"
          },
          "qs_ram": {
            "description": "RAM information",
            "type": "string",
            "example": "29550679"
          },
          "qs_cpu_model": {
            "description": "CPU model",
            "type": "string",
            "example": "Intel(R) Xeon(R) CPU E3-1271 v3 @ 3.60GHz"
          },
          "qs_cpu_mhz": {
            "description": "CPU frequency",
            "type": "string",
            "example": "2900"
          },
          "qs_location": {
            "description": "Location",
            "type": "string",
            "example": "1"
          },
          "qs_available": {
            "description": "Available information",
            "type": "string",
            "example": "0"
          },
          "qs_cost": {
            "description": "Cost",
            "type": "string",
            "example": "49"
          },
          "qs_last_update": {
            "description": "Last update date",
            "type": "string",
            "example": "2023-08-17T23:52:02.000Z"
          },
          "qs_cores": {
            "description": "Number of cores",
            "type": "string",
            "example": "8"
          },
          "qs_iowait": {
            "description": "I/O wait",
            "type": "string",
            "example": "6.89"
          },
          "qs_raid_status": {
            "description": "RAID status",
            "type": "string",
            "example": "OK: zfs:all pools are healthy"
          },
          "qs_drive_type": {
            "description": "Drive type",
            "type": "string",
            "example": "SSD"
          },
          "qs_order": {
            "description": "Order number",
            "type": "string",
            "example": "92263"
          },
          "qs_raid_building": {
            "description": "RAID building information",
            "type": "string",
            "example": "0"
          },
          "qs_kernel": {
            "description": "Kernel version",
            "type": "string",
            "example": "5.15.0-69-generic"
          },
          "qs_ioping": {
            "description": "IOPing information",
            "type": "string",
            "example": "330707348"
          },
          "qs_speed": {
            "description": "Speed information",
            "type": "string",
            "example": "1000"
          },
          "qs_distro": {
            "description": "Distribution name",
            "type": "string",
            "example": "Ubuntu"
          },
          "qs_distro_version": {
            "description": "Distribution version",
            "type": "string",
            "example": "22.04"
          },
          "qs_bytes_sec_in": {
            "description": "Bytes/sec in",
            "type": "string",
            "example": "0"
          },
          "qs_bytes_sec_out": {
            "description": "Bytes/sec out",
            "type": "string",
            "example": "0"
          },
          "qs_packets_sec_in": {
            "description": "Packets/sec in",
            "type": "string",
            "example": "0"
          },
          "qs_packets_sec_out": {
            "description": "Packets/sec out",
            "type": "string",
            "example": "0"
          },
          "qs_last_install_time": {
            "description": "Last install time (null)",
            "type": "string",
            "nullable": true
          },
          "qs_partitions": {
            "description": "Partitions information (null)",
            "type": "string",
            "nullable": true
          },
          "qs_cpu_flags": {
            "description": "CPU flags",
            "type": "string",
            "example": ""
          }
        }
      },
      "ReverseDnsEntries": {
        "title": "ReverseDnsEntries",
        "description": "The Reverse DNS entries.",
        "type": "object",
        "properties": {
          "ips": {
            "description": "The IPs you have access to and their current reverse dns mapping.",
            "type": "object",
            "additionalProperties": true
          }
        },
        "example": {
          "ips": {
            "1.2.3.4": "yourhost.com",
            "1.2.3.5": "anotherhost.com"
          }
        }
      },
      "ScrubIpsRowSchema": {
        "title": "Scrub Ips list",
        "type": "object",
        "properties": {
          "scrub_ip_id": {
            "type": "integer"
          },
          "repeat_invoices_cost": {
            "type": "number"
          },
          "scrub_ip_ip": {
            "type": "string"
          },
          "scrub_ip_status": {
            "type": "string"
          },
          "services_name": {
            "type": "string"
          }
        }
      },
      "ScrubIpsLogRowSchema": {
        "title": "Scrub Ips logs",
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "filter": {
            "type": "string"
          },
          "blocked_ip": {
            "type": "string"
          },
          "target_ip": {
            "type": "string"
          },
          "target_port": {
            "type": "number"
          },
          "protocol": {
            "type": "string"
          },
          "byte_count": {
            "type": "number"
          },
          "xdp_action": {
            "type": "string"
          }
        }
      },
      "ScrubIpFilterTypes": {
        "description": "Available scrub filter types for building firewall rules.",
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "filters": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "desc": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Server": {
        "required": [
          "serviceInfo",
          "extraInfoTables",
          "networkInfo",
          "ipmiAuth",
          "client_links",
          "billingDetails",
          "custCurrency",
          "custCurrencySymbol",
          "package",
          "serviceExtra",
          "locations"
        ],
        "type": "object",
        "properties": {
          "ipmiAuth": {
            "type": "boolean"
          },
          "client_links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServerClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/ServerBillingDetails"
          },
          "custCurrency": {
            "type": "string"
          },
          "custCurrencySymbol": {
            "type": "string"
          },
          "package": {
            "type": "string"
          },
          "serviceExtra": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "locations": {
            "type": "object",
            "properties": {
              "1": {
                "$ref": "#/components/schemas/ServerLocation1"
              }
            }
          },
          "networkInfo": {
            "$ref": "#/components/schemas/ServerNetworkInfo"
          },
          "extraInfoTables": {
            "$ref": "#/components/schemas/ServerExtraInfoTables"
          },
          "serviceInfo": {
            "$ref": "#/components/schemas/ServerServiceInfo"
          }
        }
      },
      "ServerAsset": {
        "required": [
          "id",
          "order_id",
          "hostname",
          "status",
          "primary_ipv4",
          "primary_ipv6",
          "datacenter",
          "type_id",
          "asset_tag",
          "rack",
          "row",
          "col",
          "unit_start",
          "unit_end",
          "unit_sub",
          "ipmi_mac",
          "ipmi_ip",
          "ipmi_working",
          "company",
          "comments",
          "make",
          "model",
          "description",
          "customer_id",
          "external_id",
          "billing_status",
          "overdue",
          "asset_id",
          "asset_name",
          "rack_id",
          "rack_name",
          "rack_location",
          "rack_size",
          "rack_x",
          "rack_y",
          "switchports",
          "vlans",
          "vlans6",
          "lease"
        ],
        "type": "object",
        "properties": {
          "id": {
            "description": "Unique identifier for the asset.",
            "type": "integer",
            "example": 3497
          },
          "order_id": {
            "description": "Order identifier for the asset.",
            "type": "string",
            "example": "16058"
          },
          "hostname": {
            "description": "Hostname associated with the asset.",
            "type": "string",
            "example": "myserver.host.com"
          },
          "status": {
            "description": "Status of the asset.",
            "type": "string",
            "example": "active"
          },
          "primary_ipv4": {
            "description": "Primary IPv4 address of the asset.",
            "type": "string",
            "example": "1.2.3.250"
          },
          "primary_ipv6": {
            "description": "Primary IPv6 address of the asset.",
            "type": "string",
            "example": ""
          },
          "mac": {
            "description": "MAC address associated with the asset.",
            "type": "string"
          },
          "datacenter": {
            "description": "Datacenter identifier for the asset.",
            "type": "string",
            "example": "2"
          },
          "type_id": {
            "description": "Type identifier for the asset.",
            "type": "string",
            "example": "1"
          },
          "asset_tag": {
            "description": "Asset tag associated with the asset.",
            "type": "string",
            "example": ""
          },
          "rack": {
            "description": "Rack identifier for the asset.",
            "type": "string",
            "example": "68"
          },
          "row": {
            "description": "Row identifier for the asset.",
            "type": "string",
            "example": "017"
          },
          "col": {
            "description": "Column identifier for the asset.",
            "type": "string",
            "example": "06"
          },
          "unit_start": {
            "description": "Starting unit identifier for the asset.",
            "type": "string",
            "example": "37"
          },
          "unit_end": {
            "description": "Ending unit identifier for the asset.",
            "type": "string",
            "example": "37"
          },
          "unit_sub": {
            "description": "Subunit identifier for the asset.",
            "type": "string",
            "example": "0"
          },
          "ipmi_mac": {
            "description": "IPMI MAC address associated with the asset.",
            "type": "string",
            "example": "0c:c4:7a:af:35:00"
          },
          "ipmi_ip": {
            "description": "IPMI IP address associated with the asset.",
            "type": "string",
            "example": "10.8.69.7"
          },
          "ipmi_admin_username": {
            "description": "IPMI admin username associated with the asset.",
            "type": "string"
          },
          "ipmi_admin_password": {
            "description": "IPMI admin password associated with the asset.",
            "type": "string"
          },
          "ipmi_client_username": {
            "description": "IPMI client username associated with the asset.",
            "type": "string"
          },
          "ipmi_client_password": {
            "description": "IPMI client password associated with the asset.",
            "type": "string"
          },
          "ipmi_updated": {
            "description": "IPMI update status associated with the asset.",
            "type": "string"
          },
          "ipmi_working": {
            "description": "IPMI working status associated with the asset.",
            "type": "string",
            "example": "0"
          },
          "company": {
            "description": "Company associated with the asset.",
            "type": "string",
            "example": "int"
          },
          "comments": {
            "description": "Comments associated with the asset.",
            "type": "string",
            "example": ""
          },
          "make": {
            "description": "Make of the asset.",
            "type": "string",
            "example": "Supermicro"
          },
          "model": {
            "description": "Model of the asset.",
            "type": "string",
            "example": "SYS-6018R-TDW"
          },
          "description": {
            "description": "Description of the asset.",
            "type": "string",
            "example": ""
          },
          "customer_id": {
            "description": "Customer identifier for the asset.",
            "type": "string",
            "example": "int5377"
          },
          "external_id": {
            "description": "External identifier for the asset.",
            "type": "string",
            "example": ""
          },
          "billing_status": {
            "description": "Billing status of the asset.",
            "type": "string",
            "example": "active"
          },
          "overdue": {
            "description": "Overdue status of the asset.",
            "type": "string",
            "example": "0"
          },
          "create_timestamp": {
            "description": "Timestamp of asset creation.",
            "type": "string"
          },
          "update_timestamp": {
            "description": "Timestamp of asset update.",
            "type": "string"
          },
          "asset_id": {
            "description": "Asset identifier for the asset.",
            "type": "string",
            "example": "1"
          },
          "asset_name": {
            "description": "Name of the asset.",
            "type": "string",
            "example": "server"
          },
          "rack_id": {
            "description": "Rack identifier for the asset.",
            "type": "string",
            "example": "68"
          },
          "rack_name": {
            "description": "Rack name associated with the asset.",
            "type": "string",
            "example": "112.16"
          },
          "rack_location": {
            "description": "Location of the rack associated with the asset.",
            "type": "string",
            "example": "2"
          },
          "rack_size": {
            "description": "Size of the rack associated with the asset.",
            "type": "string",
            "example": "44"
          },
          "rack_x": {
            "description": "X-coordinate of the asset within the rack.",
            "type": "string",
            "example": "25"
          },
          "rack_y": {
            "description": "Y-coordinate of the asset within the rack.",
            "type": "string",
            "example": "5"
          },
          "comment": {
            "description": "Comment associated with the asset.",
            "type": "string"
          },
          "switchports": {
            "description": "List of switchports associated with the asset.",
            "type": "array",
            "items": {
              "type": "integer"
            },
            "example": [
              10414
            ]
          },
          "vlans": {
            "description": "List of VLANs associated with the asset.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "vlans6": {
            "description": "List of IPv6 VLANs associated with the asset.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "lease": {
            "$ref": "#/components/schemas/ServerLease"
          }
        }
      },
      "ServerAssets": {
        "type": "object",
        "properties": {
          "title": {
            "description": "The title of the assets.",
            "type": "string",
            "example": "Assets"
          },
          "size": {
            "description": "The size of the assets.",
            "type": "integer",
            "example": 2
          },
          "type": {
            "description": "The type of the assets.",
            "type": "string",
            "example": "table"
          },
          "header": {
            "description": "The header of the assets table.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rows": {
            "description": "The rows of the assets table.",
            "type": "array",
            "items": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        "example": {
          "title": "Assets",
          "size": 2,
          "type": "table",
          "header": [
            "Id",
            "Hostname",
            "Description",
            "Location Name",
            "Rack Name",
            "Status",
            "Primary Ipv4",
            "Comments"
          ],
          "rows": [
            [
              "3497",
              "myserver.host.com",
              "CPU:..............2x Intel Xeon E5-2620v4 @2.10GHz\r\nMemory:...........128GB DDR3 1600MHz\r\nHard Drive 1:.....Crucial 500 GB SSD\r\nHard Drive 2:.....Crucial 500 GB SSD\r\nRAID:.............none\r\nOS:...............Ubuntu 20\r\nControl Panel:....none\r\nNetwork...........10G Card\r\nIP(s):............Vlan 3497\r\nBandwidth:........10Tb @ 10gb port",
              "TEB2",
              "112.16",
              "active",
              "1.2.3.250",
              ""
            ]
          ]
        }
      },
      "ServerBillingDetails": {
        "description": "Billing information for a dedicated server service including payment status, billing cycle, and cost.",
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "type": "string"
          },
          "service_payment_status": {
            "type": "string"
          },
          "service_frequency": {
            "type": "string"
          },
          "next_date": {
            "type": "string"
          },
          "service_next_invoice_date": {
            "type": "string"
          },
          "service_currency": {
            "type": "string"
          },
          "service_currency_symbol": {
            "type": "string"
          },
          "service_cost_info": {
            "type": "string"
          },
          "service_extra": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "service_extra_json": {
            "type": "string"
          }
        }
      },
      "ServerClientLink": {
        "required": [
          "label",
          "link"
        ],
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "link": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "icon_text": {
            "type": "string"
          },
          "help_text": {
            "type": "string"
          }
        }
      },
      "ServerExtraInfoTables": {
        "required": [
          "assets"
        ],
        "type": "object",
        "properties": {
          "assets": {
            "$ref": "#/components/schemas/ServerAssets"
          }
        }
      },
      "ServerLease": {
        "required": [
          "mac",
          "authenticated",
          "group"
        ],
        "type": "object",
        "properties": {
          "mac": {
            "description": "MAC address associated with the lease.",
            "type": "string",
            "example": "0c:c4:7a:af:35:00"
          },
          "authenticated": {
            "description": "Indicates if the lease is authenticated.",
            "type": "boolean",
            "example": false
          },
          "group": {
            "description": "Group identifier for the lease.",
            "type": "string",
            "example": "1"
          }
        }
      },
      "ServerLocation1": {
        "required": [
          "location_id",
          "location_name",
          "location_lat",
          "location_long"
        ],
        "type": "object",
        "properties": {
          "location_id": {
            "type": "integer"
          },
          "location_name": {
            "type": "string"
          },
          "location_description": {
            "type": "string"
          },
          "location_lat": {
            "type": "string"
          },
          "location_long": {
            "type": "string"
          },
          "location_ipmi_group": {
            "type": "integer"
          }
        }
      },
      "ServerNetworkInfo": {
        "required": [
          "vlans",
          "vlans6",
          "assets",
          "switchports"
        ],
        "type": "object",
        "properties": {
          "vlans": {
            "description": "List of VLANs.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "vlans6": {
            "description": "List of IPv6 VLANs.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "assets": {
            "description": "Object containing asset information.",
            "required": [
              "3497"
            ],
            "type": "object",
            "properties": {
              "3497": {
                "$ref": "#/components/schemas/ServerAsset"
              }
            }
          },
          "switchports": {
            "description": "Object containing switchport information.",
            "required": [
              "10414"
            ],
            "type": "object",
            "properties": {
              "10414": {
                "$ref": "#/components/schemas/ServerSwitchport"
              }
            }
          }
        }
      },
      "ServerOrder": {
        "description": "Object representing a server order.",
        "type": "object",
        "properties": {
          "form_values": {
            "$ref": "#/components/schemas/ServerOrderFormValues"
          },
          "config_ids": {
            "$ref": "#/components/schemas/ServerOrderConfigIds"
          },
          "cpu": {
            "description": "Number of CPUs for the server order.",
            "type": "integer",
            "example": 184
          },
          "field_label": {
            "$ref": "#/components/schemas/ServerOrderFieldLabels"
          },
          "cpu_li": {
            "description": "CPU options for the server order.",
            "type": "object",
            "properties": {
              "254": {
                "$ref": "#/components/schemas/ServerOrderCPU"
              }
            }
          },
          "memory_li": {
            "description": "Memory options for the server order.",
            "type": "object",
            "properties": {
              "254": {
                "type": "object",
                "properties": {
                  "65": {
                    "$ref": "#/components/schemas/ServerOrderMemory"
                  }
                }
              }
            }
          },
          "bandwidth_li": {
            "description": "Bandwidth options for the server order.",
            "type": "object",
            "properties": {
              "15": {
                "$ref": "#/components/schemas/ServerOrderBandwidth"
              }
            }
          },
          "ips_li": {
            "description": "IP options for the server order.",
            "type": "object",
            "properties": {
              "9": {
                "$ref": "#/components/schemas/ServerOrderIP"
              }
            }
          },
          "os_li": {
            "description": "Operating System options for the server order.",
            "type": "object",
            "properties": {
              "51": {
                "$ref": "#/components/schemas/ServerOrderOS"
              }
            }
          },
          "cp_li": {
            "description": "Control Panel options for the server order.",
            "type": "object",
            "properties": {
              "9": {
                "$ref": "#/components/schemas/ServerOrderControlPanel"
              }
            }
          },
          "raid_li": {
            "description": "RAID options for the server order.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ServerOrderRAID"
            }
          }
        }
      },
      "ServerOrderBandwidth": {
        "description": "A bandwidth option available when ordering a dedicated server.",
        "type": "object",
        "properties": {
          "id": {
            "description": "Bandwidth ID.",
            "type": "string",
            "example": "15"
          },
          "price": {
            "description": "Bandwidth price.",
            "type": "integer",
            "example": 0
          },
          "img": {
            "description": "Bandwidth image.",
            "type": "string",
            "example": "bandwidth.jpg"
          },
          "short_desc": {
            "description": "Short description of the bandwidth.",
            "type": "string",
            "example": "150TB (10Gb Port)"
          },
          "long_desc": {
            "description": "Long description of the bandwidth.",
            "type": "string",
            "example": " - 2000GB Bandwidth..."
          },
          "type": {
            "description": "Bandwidth type.",
            "type": "string",
            "example": "Standard"
          },
          "qty": {
            "description": "Quantity of bandwidth.",
            "type": "string",
            "example": "4000"
          },
          "active": {
            "description": "Active status.",
            "type": "string",
            "example": "1"
          },
          "monthly_price": {
            "description": "Monthly price.",
            "type": "integer",
            "example": 15
          },
          "price_display": {
            "description": "Display of bandwidth price.",
            "type": "string",
            "example": "$0.00"
          },
          "monthly_price_display": {
            "description": "Display of monthly bandwidth price.",
            "type": "string",
            "example": "$15.00"
          }
        }
      },
      "ServerOrderCPU": {
        "description": "A CPU option available when ordering a dedicated server.",
        "type": "object",
        "properties": {
          "id": {
            "description": "CPU ID.",
            "type": "string",
            "example": "254"
          },
          "price": {
            "description": "CPU price.",
            "type": "integer",
            "example": 0
          },
          "img": {
            "description": "CPU image.",
            "type": "string",
            "example": "ryzen.png"
          },
          "short_desc": {
            "description": "Short description of the CPU.",
            "type": "string",
            "example": "AMD RYZEN 7900X"
          },
          "long_desc": {
            "description": "Long description of the CPU.",
            "type": "string",
            "example": "High core and thread count..."
          },
          "location": {
            "description": "Location of the CPU.",
            "type": "string",
            "example": "New York"
          },
          "fsb": {
            "description": "Front Side Bus information.",
            "type": "string"
          },
          "manu": {
            "description": "Manufacturer information.",
            "type": "string"
          },
          "type": {
            "description": "CPU type.",
            "type": "string",
            "example": "AMD"
          },
          "speed": {
            "description": "CPU speed.",
            "type": "string",
            "example": "4.7"
          },
          "cache": {
            "description": "Cache information.",
            "type": "string"
          },
          "active": {
            "description": "Active status.",
            "type": "string",
            "example": "1"
          },
          "num_cores": {
            "description": "Number of cores.",
            "type": "string",
            "example": "12"
          },
          "num_cpus": {
            "description": "Number of CPUs.",
            "type": "string",
            "example": "1"
          },
          "benchmark": {
            "description": "CPU benchmark.",
            "type": "string",
            "example": "121148"
          },
          "monthly_price": {
            "description": "Monthly price.",
            "type": "integer",
            "example": 75
          },
          "max_ram": {
            "description": "Maximum RAM supported.",
            "type": "string",
            "example": "128"
          },
          "min_ram": {
            "description": "Minimum RAM required.",
            "type": "string",
            "example": "128"
          },
          "max_lff": {
            "description": "Maximum LFF (Large Form Factor) supported.",
            "type": "string",
            "example": "4"
          },
          "max_sff": {
            "description": "Maximum SFF (Small Form Factor) supported.",
            "type": "string",
            "example": "4"
          },
          "max_nve": {
            "description": "Maximum NVMe drives supported.",
            "type": "string",
            "example": "2"
          },
          "visible": {
            "description": "Visibility status.",
            "type": "string",
            "example": "yes"
          },
          "hd_ids": {
            "description": "Hard drive IDs.",
            "type": "string"
          },
          "price_display": {
            "description": "Display of CPU price.",
            "type": "string",
            "example": "$0.00"
          },
          "monthly_price_display": {
            "description": "Display of monthly CPU price.",
            "type": "string",
            "example": "$75.00"
          }
        }
      },
      "ServerOrderConfigIds": {
        "description": "Configuration IDs for the server order.",
        "type": "object",
        "properties": {
          "memory": {
            "description": "Memory configuration ID for the server order.",
            "type": "integer",
            "example": 22
          },
          "bandwidth": {
            "description": "Bandwidth configuration ID for the server order.",
            "type": "string",
            "example": "3"
          },
          "ips": {
            "description": "IPs configuration ID for the server order.",
            "type": "string",
            "example": "9"
          },
          "os": {
            "description": "Operating System configuration ID for the server order.",
            "type": "string",
            "example": "5"
          },
          "cp": {
            "description": "Control Panel configuration ID for the server order.",
            "type": "integer",
            "example": 5
          },
          "raid": {
            "description": "RAID configuration ID for the server order.",
            "type": "string",
            "example": "0"
          },
          "hd": {
            "description": "Hard Drives configuration ID for the server order.",
            "type": "string",
            "example": "16"
          }
        }
      },
      "ServerOrderControlPanel": {
        "description": "A control panel option available when ordering a dedicated server.",
        "type": "object",
        "properties": {
          "id": {
            "description": "Control Panel ID.",
            "type": "string",
            "example": "9"
          },
          "price": {
            "description": "Control Panel price.",
            "type": "integer",
            "example": 80
          },
          "img": {
            "description": "Control Panel image.",
            "type": "string",
            "example": "cpanel.gif"
          },
          "short_desc": {
            "description": "Short description of the control panel.",
            "type": "string",
            "example": "cPanel ($45+)"
          },
          "long_desc": {
            "description": "Long description of the control panel.",
            "type": "string"
          },
          "os_type": {
            "description": "OS types compatible with the control panel.",
            "type": "string"
          },
          "monthly_price": {
            "description": "Monthly price.",
            "type": "integer",
            "example": 0
          },
          "types": {
            "description": "List of types.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "1",
              "2",
              "4",
              "5",
              "8",
              "17",
              "23",
              "30",
              "51"
            ]
          },
          "price_display": {
            "description": "Display of control panel price.",
            "type": "string",
            "example": "$80.00"
          },
          "monthly_price_display": {
            "description": "Display of monthly control panel price.",
            "type": "string",
            "example": "$0.00"
          }
        }
      },
      "ServerOrderFieldLabel": {
        "description": "A display label for a field in the server order form.",
        "type": "object",
        "properties": {
          "name": {
            "description": "Name of the field label.",
            "type": "string",
            "example": "Bandwidth"
          },
          "active": {
            "description": "Active status of the field label.",
            "type": "integer",
            "example": 1
          }
        }
      },
      "ServerOrderFieldLabels": {
        "description": "Field labels for the server order.",
        "type": "object",
        "properties": {
          "bandwidth": {
            "description": "Bandwidth field label.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServerOrderFieldLabel"
              }
            ]
          },
          "ips": {
            "description": "IPs field label.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServerOrderFieldLabel"
              }
            ]
          },
          "os": {
            "description": "Operating System field label.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServerOrderFieldLabel"
              }
            ]
          },
          "cp": {
            "description": "Control Panel field label.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServerOrderFieldLabel"
              }
            ]
          },
          "raid": {
            "description": "RAID field label.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServerOrderFieldLabel"
              }
            ]
          },
          "memory": {
            "description": "Memory field label.",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServerOrderFieldLabel"
              }
            ]
          },
          "hd": {
            "description": "Hard Drives field label",
            "allOf": [
              {
                "$ref": "#/components/schemas/ServerOrderFieldLabel"
              }
            ]
          }
        }
      },
      "ServerOrderFormValues": {
        "description": "Form values for the server order.",
        "type": "object",
        "properties": {
          "memory": {
            "description": "Memory value for the server order.",
            "type": "integer",
            "example": 22
          },
          "bandwidth": {
            "description": "Bandwidth value for the server order.",
            "type": "string",
            "example": "3"
          },
          "ips": {
            "description": "IPs value for the server order.",
            "type": "string",
            "example": "9"
          },
          "os": {
            "description": "Operating System value for the server order.",
            "type": "string",
            "example": "5"
          },
          "cp": {
            "description": "Control Panel value for the server order.",
            "type": "integer",
            "example": 5
          },
          "raid": {
            "description": "RAID value for the server order.",
            "type": "string",
            "example": "0"
          },
          "hd": {
            "description": "Hard Drives value for the server order.",
            "type": "string",
            "example": "16"
          }
        }
      },
      "ServerOrderIP": {
        "description": "An IP block option available when ordering a dedicated server.",
        "type": "object",
        "properties": {
          "id": {
            "description": "IP ID.",
            "type": "string",
            "example": "9"
          },
          "price": {
            "description": "IP price.",
            "type": "integer",
            "example": 0
          },
          "img": {
            "description": "IP image.",
            "type": "string",
            "example": "ips.jpg"
          },
          "short_desc": {
            "description": "Short description of the IP.",
            "type": "string",
            "example": "1 Vlan Ip (/30)"
          },
          "long_desc": {
            "description": "Long description of the IP.",
            "type": "string",
            "example": "1 IP In personal Vlan"
          },
          "qty": {
            "description": "Quantity of IPs.",
            "type": "string",
            "example": "1"
          },
          "monthly_price": {
            "description": "Monthly price.",
            "type": "integer",
            "example": 0
          },
          "price_display": {
            "description": "Display of IP price.",
            "type": "string",
            "example": "$0.00"
          },
          "monthly_price_display": {
            "description": "Display of monthly IP price.",
            "type": "string",
            "example": "$0.00"
          }
        }
      },
      "ServerOrderMemory": {
        "description": "A memory (RAM) option available when ordering a dedicated server.",
        "type": "object",
        "properties": {
          "id": {
            "description": "Memory ID.",
            "type": "string",
            "example": "65"
          },
          "price": {
            "description": "Memory price.",
            "type": "string",
            "example": "0"
          },
          "img": {
            "description": "Memory image.",
            "type": "string",
            "example": "hd.jpg"
          },
          "short_desc": {
            "description": "Short description of the memory.",
            "type": "string",
            "example": "20TB SATA"
          },
          "long_desc": {
            "description": "Long description of the memory.",
            "type": "string"
          },
          "manu": {
            "description": "Manufacturer information.",
            "type": "string"
          },
          "size": {
            "description": "Memory size.",
            "type": "string",
            "example": "3000"
          },
          "type": {
            "description": "Memory type.",
            "type": "string"
          },
          "hidden": {
            "description": "Hidden status.",
            "type": "string",
            "example": "0"
          },
          "monthly_price": {
            "description": "Monthly price.",
            "type": "integer",
            "example": 50
          },
          "drive_type": {
            "description": "Drive type.",
            "type": "string",
            "example": "lff"
          },
          "monthly_price_display": {
            "description": "Display of monthly memory price.",
            "type": "string",
            "example": "$50.00"
          }
        }
      },
      "ServerOrderOS": {
        "description": "An operating system option available when ordering a dedicated server.",
        "type": "object",
        "properties": {
          "id": {
            "description": "Operating System ID.",
            "type": "string",
            "example": "51"
          },
          "price": {
            "description": "Operating System price.",
            "type": "integer",
            "example": 0
          },
          "img": {
            "description": "Operating System image.",
            "type": "string"
          },
          "short_desc": {
            "description": "Short description of the OS.",
            "type": "string",
            "example": "AlmaLinux"
          },
          "long_desc": {
            "description": "Long description of the OS.",
            "type": "string"
          },
          "monthly_price": {
            "description": "Monthly price.",
            "type": "integer",
            "example": 0
          },
          "active": {
            "description": "Active status.",
            "type": "string",
            "example": "1"
          },
          "price_display": {
            "description": "Display of OS price.",
            "type": "string",
            "example": "$0.00"
          },
          "monthly_price_display": {
            "description": "Display of monthly OS price.",
            "type": "string",
            "example": "$0.00"
          }
        }
      },
      "ServerOrderRAID": {
        "description": "A RAID configuration option available when ordering a dedicated server.",
        "type": "object",
        "properties": {
          "id": {
            "description": "RAID ID.",
            "type": "string",
            "example": "7"
          },
          "price": {
            "description": "RAID price.",
            "type": "integer",
            "example": 50
          },
          "img": {
            "description": "RAID image.",
            "type": "string",
            "example": "raid.png"
          },
          "short_desc": {
            "description": "Short description of the RAID.",
            "type": "string",
            "example": "Hardware Raid 5"
          },
          "long_desc": {
            "description": "Long description of the RAID.",
            "type": "string",
            "example": "Hardware Raid 5"
          },
          "monthly_price": {
            "description": "Monthly price.",
            "type": "integer",
            "example": 50
          },
          "active": {
            "description": "Active status.",
            "type": "string",
            "example": "1"
          },
          "price_display": {
            "description": "Display of RAID price.",
            "type": "string",
            "example": "$50.00"
          },
          "monthly_price_display": {
            "description": "Display of monthly RAID price.",
            "type": "string",
            "example": "$50.00"
          }
        }
      },
      "ServerRow": {
        "title": "ServerRow",
        "description": "A result row from the `Servers` `GET` request.",
        "required": [
          "server_status",
          "server_id",
          "server_hostname",
          "account_lid"
        ],
        "type": "object",
        "properties": {
          "server_id": {
            "description": "The id of the server.",
            "type": "string",
            "example": "8404"
          },
          "account_lid": {
            "description": "The account lid of the server.",
            "type": "string",
            "example": "detain@interserver.net"
          },
          "server_hostname": {
            "description": "The hostname of the server.",
            "type": "string",
            "example": "testsignup.is.net"
          },
          "server_status": {
            "description": "The status of the server.",
            "type": "string",
            "example": "deleted"
          }
        },
        "example": {
          "server_id": "8404",
          "account_lid": "detain@interserver.net",
          "server_hostname": "testsignup.is.net",
          "server_status": "deleted"
        }
      },
      "ServerServiceInfo": {
        "type": "object",
        "properties": {
          "server_id": {
            "description": "The ID of the server.",
            "type": "string",
            "example": "16058"
          },
          "server_hostname": {
            "description": "The hostname of the server.",
            "type": "string",
            "example": "myserver.host.com"
          },
          "server_custid": {
            "description": "The customer ID associated with the server.",
            "type": "string",
            "example": "771282"
          },
          "server_type": {
            "description": "The type of the server.",
            "type": "string",
            "example": "600"
          },
          "server_currency": {
            "description": "The currency used for billing.",
            "type": "string",
            "example": "USD"
          },
          "server_order_date": {
            "description": "The date when the server was ordered.",
            "type": "string",
            "example": "2020-05-08T17:22:36.000Z"
          },
          "server_invoice": {
            "description": "The invoice number for the server.",
            "type": "string",
            "example": "18738142"
          },
          "server_coupon": {
            "description": "The coupon associated with the server.",
            "type": "string",
            "example": "0"
          },
          "server_status": {
            "description": "The status of the server.",
            "type": "string",
            "example": "active"
          },
          "server_root": {
            "description": "The root of the server.",
            "type": "string",
            "example": ""
          },
          "server_dedicated_tag": {
            "description": "The dedicated tag of the server.",
            "type": "string",
            "example": "0"
          },
          "server_custom_tag": {
            "description": "The custom tag of the server.",
            "type": "string",
            "example": ""
          },
          "server_comment": {
            "description": "Comments related to the server.",
            "type": "string",
            "example": ""
          },
          "server_initial_bill": {
            "description": "The initial billing amount for the server.",
            "type": "string",
            "example": "0"
          },
          "server_hardware": {
            "description": "The hardware information of the server.",
            "type": "string",
            "example": "0"
          },
          "server_ips": {
            "description": "The number of IPs associated with the server.",
            "type": "string",
            "example": "0"
          },
          "server_monthly_bill": {
            "description": "The monthly billing amount for the server.",
            "type": "string",
            "example": "0"
          },
          "server_setup": {
            "description": "The setup status of the server.",
            "type": "string",
            "example": "0"
          },
          "server_discount": {
            "description": "Discount information for the server.",
            "type": "string"
          },
          "server_rep": {
            "description": "The reputation of the server.",
            "type": "string",
            "example": "0"
          },
          "server_date": {
            "description": "The date related to the server.",
            "type": "string",
            "example": "1588972956"
          },
          "server_total_cost": {
            "description": "The total cost of the server.",
            "type": "string",
            "example": "230"
          },
          "server_location": {
            "description": "The location of the server.",
            "type": "string"
          },
          "server_hardware_ordered": {
            "description": "The ordered hardware for the server.",
            "type": "string",
            "example": "0"
          },
          "server_billed": {
            "description": "The billed amount for the server.",
            "type": "string",
            "example": "0"
          },
          "server_welcome_email": {
            "description": "Indicates whether a welcome email was sent.",
            "type": "string",
            "example": "1"
          },
          "server_dedicated_cpu": {
            "description": "The number of dedicated CPUs for the server.",
            "type": "string",
            "example": "38"
          },
          "server_dedicated_memory": {
            "description": "The amount of dedicated memory for the server.",
            "type": "string",
            "example": "22"
          },
          "server_dedicated_hd1": {
            "description": "The size of the first dedicated hard drive.",
            "type": "string",
            "example": "20"
          },
          "server_dedicated_hd2": {
            "description": "The size of the second dedicated hard drive.",
            "type": "string"
          },
          "server_dedicated_bandwidth": {
            "description": "The bandwidth of the server.",
            "type": "string",
            "example": "3"
          },
          "server_dedicated_ips": {
            "description": "The number of dedicated IPs for the server.",
            "type": "string",
            "example": "5"
          },
          "server_dedicated_os": {
            "description": "The operating system of the server.",
            "type": "string",
            "example": "30"
          },
          "server_dedicated_cp": {
            "description": "The control panel of the server.",
            "type": "string"
          },
          "server_dedicated_raid": {
            "description": "The RAID configuration of the server.",
            "type": "string",
            "example": "0"
          },
          "server_extra": {
            "description": "Additional information about the server.",
            "type": "string",
            "example": "[]"
          }
        }
      },
      "ServerSwitchport": {
        "required": [
          "switchport_id",
          "switch_id",
          "switch",
          "port",
          "blade",
          "justport",
          "graph_id",
          "asset_id"
        ],
        "type": "object",
        "properties": {
          "switchport_id": {
            "description": "Unique identifier for the switchport.",
            "type": "integer",
            "example": 10414
          },
          "switch_id": {
            "description": "Unique identifier for the switch associated with the switchport.",
            "type": "string",
            "example": "118"
          },
          "switch": {
            "description": "Name of the switch associated with the switchport.",
            "type": "string",
            "example": "edge1"
          },
          "port": {
            "description": "Port name on the switch.",
            "type": "string",
            "example": "Ethernet1/33"
          },
          "blade": {
            "description": "Blade name associated with the port.",
            "type": "string",
            "example": "Ethernet1"
          },
          "justport": {
            "description": "Port identifier.",
            "type": "string",
            "example": "33"
          },
          "graph_id": {
            "description": "Identifier for the graph associated with the switchport.",
            "type": "string",
            "example": "12622"
          },
          "vlans": {
            "description": "List of VLANs associated with the switchport.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "vlans6": {
            "description": "List of IPv6 VLANs associated with the switchport.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": []
          },
          "asset_id": {
            "description": "Unique identifier of the asset associated with the switchport.",
            "type": "integer",
            "example": 3497
          }
        }
      },
      "Service": {
        "title": "Service",
        "description": "An individual package tied to one of our services.",
        "required": [
          "services_type",
          "services_name",
          "services_module",
          "services_id",
          "services_field2",
          "services_buyable",
          "services_cost",
          "services_category",
          "services_field1",
          "services_currency"
        ],
        "type": "object",
        "properties": {
          "services_id": {
            "format": "int32",
            "type": "integer",
            "example": 31
          },
          "services_name": {
            "type": "string",
            "example": "OpenVZ VPS Slice"
          },
          "services_cost": {
            "type": "number",
            "example": 6
          },
          "services_currency": {
            "type": "string",
            "example": "USD"
          },
          "services_category": {
            "format": "int32",
            "type": "integer",
            "example": 1
          },
          "services_buyable": {
            "type": "boolean",
            "example": true
          },
          "services_type": {
            "format": "int32",
            "type": "integer",
            "example": 6
          },
          "services_field1": {
            "type": "string",
            "example": "slice"
          },
          "services_field2": {
            "type": "string"
          },
          "services_module": {
            "type": "string",
            "example": "vps"
          }
        },
        "example": {
          "services_id": 31,
          "services_name": "OpenVZ VPS Slice",
          "services_cost": 6,
          "services_currency": "USD",
          "services_category": 1,
          "services_buyable": true,
          "services_type": 6,
          "services_field1": "slice",
          "services_field2": "",
          "services_module": "vps"
        }
      },
      "ServiceCategories": {
        "description": "The listing of the categories for the services.",
        "type": "object",
        "additionalProperties": {
          "$ref": "#/components/schemas/ServiceCategory"
        },
        "example": {
          "1": {
            "category_id": 1,
            "category_name": "OpenVZ Virtual Servers",
            "category_tag": "openvz",
            "category_module": "vps"
          },
          "2": {
            "category_id": 2,
            "category_name": "KVM Virtual Servers",
            "category_tag": "kvm",
            "category_module": "vps"
          },
          "3": {
            "category_id": 3,
            "category_name": "Xen Virtual Servers",
            "category_tag": "xen",
            "category_module": "vps"
          },
          "4": {
            "category_id": 4,
            "category_name": "LXC Virtual Servers",
            "category_tag": "lxc",
            "category_module": "vps"
          }
        }
      },
      "ServiceCategory": {
        "title": "ServiceCategory",
        "description": "A Category of Services.  This is the broadest grouping within a module.",
        "required": [
          "category_tag",
          "category_name",
          "category_module",
          "category_id"
        ],
        "type": "object",
        "properties": {
          "category_id": {
            "type": "integer",
            "example": 1
          },
          "category_name": {
            "type": "string",
            "example": "OpenVZ Virtual Servers"
          },
          "category_tag": {
            "type": "string",
            "example": "openvz"
          },
          "category_module": {
            "type": "string",
            "example": "vps"
          }
        },
        "example": {
          "category_id": 505,
          "category_name": "KSplice",
          "category_tag": "ksplice",
          "category_module": "licenses"
        }
      },
      "ServiceType": {
        "title": "ServiceType",
        "description": "A general grouping of services within a category.",
        "required": [
          "st_name",
          "st_module",
          "st_id",
          "st_category"
        ],
        "type": "object",
        "properties": {
          "st_id": {
            "type": "integer",
            "example": 600
          },
          "st_name": {
            "type": "string",
            "example": "Dedicated Server"
          },
          "st_category": {
            "type": "integer",
            "example": 600
          },
          "st_module": {
            "type": "string",
            "example": "servers"
          }
        },
        "example": {
          "st_id": 600,
          "st_name": "Dedicated Server",
          "st_category": 600,
          "st_module": "servers"
        }
      },
      "ServiceTypes": {
        "description": "The listing of service types",
        "type": "object",
        "additionalProperties": {
          "$ref": "#/components/schemas/ServiceType"
        },
        "example": {
          "1": {
            "st_id": 1,
            "st_name": "KVM Windows",
            "st_category": 2,
            "st_module": "vps"
          },
          "2": {
            "st_id": 2,
            "st_name": "KVM Linux",
            "st_category": 2,
            "st_module": "vps"
          },
          "3": {
            "st_id": 3,
            "st_name": "Cloud KVM Windows",
            "st_category": 3,
            "st_module": "vps"
          },
          "4": {
            "st_id": 4,
            "st_name": "Cloud KVM Linux",
            "st_category": 3,
            "st_module": "vps"
          }
        }
      },
      "Services": {
        "description": "The list of service packages.",
        "type": "object",
        "additionalProperties": {
          "$ref": "#/components/schemas/Service"
        },
        "example": {
          "31": {
            "services_id": 31,
            "services_name": "OpenVZ VPS Slice",
            "services_cost": 6,
            "services_currency": "USD",
            "services_category": 1,
            "services_buyable": true,
            "services_type": 6,
            "services_field1": "slice",
            "services_field2": "",
            "services_module": "vps"
          },
          "32": {
            "services_id": 32,
            "services_name": "KVM Windows VPS Slice",
            "services_cost": 10,
            "services_currency": "USD",
            "services_category": 2,
            "services_buyable": true,
            "services_type": 1,
            "services_field1": "slice",
            "services_field2": "",
            "services_module": "vps"
          },
          "33": {
            "services_id": 33,
            "services_name": "KVM Linux VPS Slice",
            "services_cost": 6,
            "services_currency": "USD",
            "services_category": 2,
            "services_buyable": true,
            "services_type": 2,
            "services_field1": "slice",
            "services_field2": "",
            "services_module": "vps"
          }
        }
      },
      "ServicesInfo": {
        "description": "Contains the complete catalog of available modules, services, service types, and service categories.",
        "required": [
          "modules",
          "services",
          "serviceTypes",
          "serviceCategories"
        ],
        "type": "object",
        "properties": {
          "modules": {
            "$ref": "#/components/schemas/Modules"
          },
          "services": {
            "$ref": "#/components/schemas/Services"
          },
          "serviceTypes": {
            "$ref": "#/components/schemas/ServiceTypes"
          },
          "serviceCategories": {
            "$ref": "#/components/schemas/ServiceCategories"
          }
        },
        "example": {
          "modules": {
            "domains": {
              "SERVICE_ID_OFFSET": 10000,
              "USE_REPEAT_INVOICE": true,
              "USE_PACKAGES": true,
              "BILLING_DAYS_OFFSET": 45,
              "IMGNAME": "domain.png",
              "REPEAT_BILLING_METHOD": 2,
              "DELETE_PENDING_DAYS": 45,
              "SUSPEND_DAYS": 14,
              "SUSPEND_WARNING_DAYS": 7,
              "TITLE": "Domain Registrations",
              "MENUNAME": "Domains",
              "EMAIL_FROM": "support@interserver.net",
              "TBLNAME": "Domains",
              "TABLE": "domains",
              "TITLE_FIELD": "domain_hostname",
              "PREFIX": "domain"
            },
            "vps": {
              "SERVICE_ID_OFFSET": 0,
              "USE_REPEAT_INVOICE": true,
              "USE_PACKAGES": true,
              "BILLING_DAYS_OFFSET": 0,
              "IMGNAME": "root-server.png",
              "REPEAT_BILLING_METHOD": 2,
              "DELETE_PENDING_DAYS": 45,
              "SUSPEND_DAYS": 14,
              "SUSPEND_WARNING_DAYS": 7,
              "TITLE": "VPS",
              "MENUNAME": "VPS",
              "EMAIL_FROM": "support@interserver.net",
              "TBLNAME": "VPS",
              "TABLE": "vps",
              "TITLE_FIELD": "vps_hostname",
              "TITLE_FIELD2": "vps_ip",
              "TITLE_FIELD3": "vps_vzid",
              "PREFIX": "vps"
            }
          },
          "services": {
            "31": {
              "services_id": 31,
              "services_name": "OpenVZ VPS Slice",
              "services_cost": 6,
              "services_currency": "USD",
              "services_category": 1,
              "services_buyable": true,
              "services_type": 6,
              "services_field1": "slice",
              "services_field2": "",
              "services_module": "vps"
            },
            "32": {
              "services_id": 32,
              "services_name": "KVM Windows VPS Slice",
              "services_cost": 10,
              "services_currency": "USD",
              "services_category": 2,
              "services_buyable": true,
              "services_type": 1,
              "services_field1": "slice",
              "services_field2": "",
              "services_module": "vps"
            }
          },
          "serviceTypes": {
            "1": {
              "st_id": 1,
              "st_name": "KVM Windows",
              "st_category": 2,
              "st_module": "vps"
            },
            "2": {
              "st_id": 2,
              "st_name": "KVM Linux",
              "st_category": 2,
              "st_module": "vps"
            },
            "3": {
              "st_id": 3,
              "st_name": "Cloud KVM Windows",
              "st_category": 3,
              "st_module": "vps"
            }
          },
          "serviceCategories": {
            "1": {
              "category_id": 1,
              "category_name": "OpenVZ Virtual Servers",
              "category_tag": "openvz",
              "category_module": "vps"
            },
            "2": {
              "category_id": 2,
              "category_name": "KVM Virtual Servers",
              "category_tag": "kvm",
              "category_module": "vps"
            },
            "3": {
              "category_id": 3,
              "category_name": "Xen Virtual Servers",
              "category_tag": "xen",
              "category_module": "vps"
            }
          }
        }
      },
      "SuccessTextResponse": {
        "title": "SuccessTextResponse",
        "description": "Response with success flag and text description.",
        "required": [
          "success"
        ],
        "type": "object",
        "properties": {
          "success": {
            "description": "Indicates whether or not the command was successful or not.",
            "type": "boolean"
          },
          "text": {
            "description": "Text associated with the response.",
            "type": "string"
          },
          "action": {
            "description": "Optional Action relating to the response.",
            "type": "string"
          }
        },
        "example": {
          "success": true,
          "text": "Ok"
        }
      },
      "TemplateRequest": {
        "title": "TemplateRequest",
        "description": "VPS OS Template Request",
        "required": [
          "template",
          "localPassword"
        ],
        "type": "object",
        "properties": {
          "template": {
            "description": "OS Template Filename",
            "type": "string",
            "example": "ubuntu24"
          },
          "password": {
            "format": "password",
            "description": "Password for Root / Administrator Account.",
            "minLength": 6,
            "type": "string",
            "example": "myUserPassword"
          },
          "localPassword": {
            "format": "password",
            "description": "Password for this account.",
            "type": "string"
          }
        },
        "example": {
          "template": "ubuntu24",
          "localPassword": "myUserPassword"
        }
      },
      "TextResponse": {
        "title": "TextResponse",
        "description": "Text Response Object",
        "type": "object",
        "properties": {
          "text": {
            "description": "Response text",
            "type": "string",
            "example": "\"You were successfull.\""
          },
          "message": {
            "description": "Response message",
            "type": "string"
          }
        },
        "example": {
          "text": "You were successfull."
        }
      },
      "Tickets": {
        "title": "Tickets",
        "description": "A listing of support tickets.",
        "required": [
          "tickets",
          "viewText",
          "view",
          "sortdir",
          "sortcol",
          "pages",
          "limit",
          "rowsOffset",
          "rowsTotal",
          "inboxCount",
          "ima",
          "custid",
          "currentPage",
          "countArray"
        ],
        "type": "object",
        "properties": {
          "ima": {
            "type": "string"
          },
          "custid": {
            "type": "string"
          },
          "view": {
            "type": "string"
          },
          "currentPage": {
            "format": "int32",
            "type": "integer"
          },
          "limit": {
            "format": "int32",
            "type": "integer"
          },
          "sortcol": {
            "format": "int32",
            "type": "integer"
          },
          "sortdir": {
            "format": "int32",
            "type": "integer"
          },
          "rowsOffset": {
            "format": "int32",
            "type": "integer"
          },
          "tickets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TicketsRow"
            }
          },
          "pages": {
            "format": "int32",
            "type": "integer"
          },
          "rowsTotal": {
            "format": "int32",
            "type": "integer"
          },
          "inboxCount": {
            "format": "int32",
            "type": "integer"
          },
          "countArray": {
            "properties": {
              "Open": {
                "format": "int32",
                "type": "integer"
              },
              "On Hold": {
                "format": "int32",
                "type": "integer"
              },
              "Closed": {
                "format": "int32",
                "type": "integer"
              }
            }
          },
          "viewText": {
            "type": "string"
          }
        },
        "example": {
          "ima": "client",
          "custid": "223513",
          "view": "Open",
          "currentPage": 1,
          "limit": 50,
          "sortcol": 6,
          "sortdir": 1,
          "rowsOffset": 0,
          "tickets": [],
          "pages": 7,
          "rowsTotal": 311,
          "inboxCount": 311,
          "countArray": {
            "Open": 3,
            "On Hold": 3,
            "Closed": 305
          },
          "viewText": "Inbox"
        }
      },
      "TicketsRow": {
        "title": "TicketsRow",
        "description": "Information about a single ticket.",
        "required": [
          "attachments",
          "total_replies",
          "title",
          "ticketmaskid",
          "ticketid",
          "status_text",
          "status",
          "priority",
          "lastreplier",
          "lastactivity",
          "departmenttitle",
          "checked",
          "can_close"
        ],
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "ticketmaskid": {
            "type": "string"
          },
          "lastreplier": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "priority": {
            "type": "string"
          },
          "total_replies": {
            "format": "int32",
            "type": "integer"
          },
          "lastactivity": {
            "type": "string"
          },
          "departmenttitle": {
            "type": "string"
          },
          "ticketid": {
            "format": "int32",
            "type": "integer"
          },
          "can_close": {
            "type": "string"
          },
          "attachments": {},
          "status_text": {
            "type": "string"
          },
          "checked": {
            "type": "boolean"
          }
        },
        "example": {
          "title": "fix reprocessing of messages on (re)connect",
          "ticketmaskid": "TLT-898-78119",
          "lastreplier": "Joe",
          "status": "<span class=\"badge badge-danger\">Closed</span>",
          "priority": "Standard - 1 to 3 hour resolution",
          "total_replies": 2,
          "lastactivity": "3 years ago",
          "departmenttitle": "Bugs",
          "ticketid": 704185,
          "can_close": "no",
          "attachments": [],
          "status_text": "Closed",
          "checked": false
        }
      },
      "TimezoneUpdate": {
        "title": "TimezoneUpdate",
        "description": "The request to update the time zone of a vps.",
        "required": [
          "timezone"
        ],
        "type": "object",
        "properties": {
          "timezone": {
            "description": "The time zone",
            "type": "string",
            "example": "America/New_York"
          }
        },
        "example": {
          "timezone": "America/New_York"
        }
      },
      "UrlRequest": {
        "title": "UrlRequest",
        "description": "URL",
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          }
        },
        "example": {
          "url": "https://templates.is.cc/systemrescuecd/systemrescue-7.01-amd64.iso"
        }
      },
      "VPSTrafficDataDataSectionResponse": {
        "title": "VPSTrafficDataDataSectionResponse",
        "description": "VPS Traffic Data section Data subsection Row Response",
        "type": "array",
        "items": {
          "oneOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "integer"
            }
          ]
        },
        "example": [
          "2023-09-17T03:30:30Z",
          3968
        ]
      },
      "Vps": {
        "required": [
          "serviceInfo",
          "client_links",
          "billingDetails",
          "custCurrency",
          "custCurrencySymbol",
          "serviceMaster",
          "package",
          "serviceExtra",
          "extraInfoTables",
          "module",
          "token",
          "da_link",
          "sr_link",
          "cp_data",
          "da_data",
          "plesk12_data",
          "serviceAddons"
        ],
        "type": "object",
        "properties": {
          "serviceInfo": {
            "$ref": "#/components/schemas/VpsServiceInfo"
          },
          "client_links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VpsClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/VpsBillingDetails"
          },
          "custCurrency": {
            "type": "string"
          },
          "custCurrencySymbol": {
            "type": "string"
          },
          "serviceMaster": {
            "$ref": "#/components/schemas/VpsServiceMaster"
          },
          "package": {
            "type": "string"
          },
          "os_template": {
            "type": "string"
          },
          "serviceExtra": {
            "$ref": "#/components/schemas/VpsServiceExtra"
          },
          "extraInfoTables": {
            "$ref": "#/components/schemas/VpsExtraInfoTables"
          },
          "cpu_graph_data": {},
          "module": {
            "type": "string"
          },
          "token": {
            "type": "string"
          },
          "da_link": {
            "type": "integer"
          },
          "sr_link": {
            "type": "integer"
          },
          "cp_data": {
            "$ref": "#/components/schemas/VpsCPData"
          },
          "da_data": {
            "$ref": "#/components/schemas/VpsDAData"
          },
          "plesk12_data": {
            "$ref": "#/components/schemas/VpsPlesk12Data"
          },
          "serviceAddons": {
            "$ref": "#/components/schemas/VpsServiceAddons"
          }
        }
      },
      "VpsBackupRow": {
        "title": "VpsBackupRow",
        "description": "A single backed-up item and information about it.",
        "required": [
          "type",
          "size",
          "date",
          "name",
          "service"
        ],
        "type": "object",
        "properties": {
          "type": {
            "description": "Backup Type",
            "type": "string",
            "example": "zfs"
          },
          "service": {
            "description": "The service id such as vps  id.",
            "type": "integer",
            "example": 12343
          },
          "name": {
            "description": "The name of the backup.",
            "type": "string",
            "example": "automated_backup"
          },
          "size": {
            "description": "Size of the file in bytes",
            "type": "integer",
            "example": 132412343124213
          },
          "date": {
            "format": "int32",
            "description": "The creation date of the backup in a unix timestamp.",
            "type": "integer",
            "example": 1693996140
          }
        },
        "example": {
          "type": "zfs",
          "service": 242828,
          "name": "third",
          "size": 514850816,
          "date": 1693909500
        }
      },
      "VpsBackupRows": {
        "title": "VpsBackupRows",
        "description": "The listing of the backups for your service.",
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/VpsBackupRow"
        },
        "example": [
          {
            "type": "zfs",
            "service": 2722890,
            "name": "first",
            "size": 3229615,
            "date": 1694082660
          },
          {
            "type": "swift",
            "service": 100,
            "name": "reset_shot_vps100_20210928.jpg",
            "size": 40621,
            "date": 1632842251
          },
          {
            "type": "swift",
            "service": 100,
            "name": "reset_shot_vps100_20210929.jpg",
            "size": 36319,
            "date": 1632968243
          }
        ]
      },
      "VpsBillingDetails": {
        "description": "Billing information for a VPS service including payment status, billing cycle, and cost.",
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "description": "Last invoice date",
            "type": "string",
            "example": "July 26, 2023"
          },
          "service_payment_status": {
            "description": "Payment status",
            "type": "string",
            "example": "Paid"
          },
          "service_frequency": {
            "description": "Billing frequency",
            "type": "string",
            "example": "Monthly"
          },
          "next_date": {
            "description": "Next billing date",
            "type": "string",
            "example": "2023-08-26T09:41:12.000Z"
          },
          "service_next_invoice_date": {
            "description": "Next invoice date",
            "type": "string",
            "example": "August 26, 2023"
          },
          "service_currency": {
            "description": "Currency used for billing",
            "type": "string",
            "example": "USD"
          },
          "service_currency_symbol": {
            "description": "Currency symbol",
            "type": "string",
            "example": "$"
          },
          "service_coupon": {
            "description": "Billing coupon code",
            "type": "string",
            "example": "TheCouponIUsed"
          },
          "service_cost_info": {
            "description": "Cost information",
            "type": "string",
            "example": "0.00"
          },
          "service_extra": {
            "$ref": "#/components/schemas/VpsServiceExtra"
          },
          "service_extra_json": {
            "description": "Additional information in JSON format",
            "type": "string",
            "example": "{\"spice\":5903,\"snapshots\":[{\"name\":\"third\",\"used\":36490445,\"date\":1692095220},{\"name\":\"second\",\"used\":40894464,\"date\":1692181620},{\"name\":\"first\",\"used\":54735668,\"date\":1692268020}]}"
          }
        }
      },
      "VpsCPData": {
        "description": "Control panel license options available for a VPS.",
        "type": "object",
        "properties": {
          "name": {
            "description": "Control panel name (e.g., cPanel).",
            "type": "string"
          },
          "cost": {
            "description": "Monthly cost in cents for the control panel license.",
            "type": "integer"
          }
        }
      },
      "VpsClientLink": {
        "description": "A navigation link for VPS-related actions in the client portal.",
        "type": "object",
        "properties": {
          "label": {
            "description": "Display label for the link.",
            "type": "string"
          },
          "link": {
            "description": "URL or route for the action.",
            "type": "string"
          },
          "icon": {
            "description": "Icon class for the link.",
            "type": "string"
          },
          "icon_text": {
            "description": "Icon text label.",
            "type": "string"
          },
          "help_text": {
            "description": "Help tooltip text for the link.",
            "type": "string"
          }
        }
      },
      "VpsDAData": {
        "description": "DirectAdmin license options available for a VPS.",
        "type": "object",
        "properties": {
          "free": {
            "$ref": "#/components/schemas/VpsDALicense"
          }
        }
      },
      "VpsDALicense": {
        "description": "A DirectAdmin license tier option.",
        "type": "object",
        "properties": {
          "name": {
            "description": "License tier name.",
            "type": "string"
          },
          "sub_name": {
            "description": "License tier sub-name.",
            "type": "string"
          },
          "cost": {
            "description": "Monthly cost in cents.",
            "type": "integer"
          },
          "img_disabled": {
            "description": "Image path for the disabled state icon.",
            "type": "string"
          },
          "img_active": {
            "description": "Image path for the active state icon.",
            "type": "string"
          }
        }
      },
      "VpsExtraInfoTables": {
        "description": "Additional informational tables displayed for a VPS service.",
        "type": "object",
        "properties": {
          "ip_info": {
            "$ref": "#/components/schemas/VpsIPInfo"
          }
        }
      },
      "VpsIPInfo": {
        "description": "IP address information table for a VPS service.",
        "type": "object",
        "properties": {
          "title": {
            "description": "Title of the table",
            "type": "string",
            "example": "IP Information"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VpsIPInfoRow"
            }
          }
        }
      },
      "VpsIPInfoRow": {
        "type": "object",
        "properties": {
          "desc": {
            "description": "Description",
            "type": "string"
          },
          "value": {
            "description": "Value",
            "type": "string"
          }
        }
      },
      "VpsOrder": {
        "description": "Pricing and configuration options for ordering a new VPS, including slice costs by platform and available OS templates.",
        "required": [
          "vpsSliceSsdOvzCost",
          "vpsSliceOvzCost",
          "vpsSliceSsdVirtuozzoCost",
          "vpsSliceVirtuozzoCost",
          "vpsSliceHypervCost",
          "vpsSliceVmwareCost",
          "vpsSliceLxcCost",
          "vpsSliceXenCost",
          "vpsSliceKvmLCost",
          "vpsSliceKvmStorageCost",
          "vpsNyCost",
          "vpsSliceKvmWCost",
          "cpanelCost",
          "daCost",
          "ramSlice",
          "hdSlice",
          "hdStorageSlice",
          "bwSlice",
          "bwType",
          "bwTotal",
          "maxSlices",
          "platformPackages",
          "platformNames",
          "packageCosts",
          "locationStock",
          "locationNames",
          "osNames",
          "templates",
          "serviceTypes",
          "currency",
          "currencySymbol"
        ],
        "type": "object",
        "properties": {
          "vpsSliceSsdOvzCost": {
            "description": "Cost of VPS Slice SSD OVZ",
            "type": "number",
            "example": 9
          },
          "vpsSliceOvzCost": {
            "description": "Cost of VPS Slice OVZ",
            "type": "number",
            "example": 6
          },
          "vpsSliceSsdVirtuozzoCost": {
            "description": "Cost of VPS Slice SSD Virtuozzo",
            "type": "number",
            "example": 9
          },
          "vpsSliceVirtuozzoCost": {
            "description": "Cost of VPS Slice Virtuozzo",
            "type": "number",
            "example": 6
          },
          "vpsSliceHypervCost": {
            "description": "Cost of VPS Slice HyperV",
            "type": "number",
            "example": 10
          },
          "vpsSliceVmwareCost": {
            "description": "Cost of VPS Slice VMware",
            "type": "number",
            "example": 10
          },
          "vpsSliceLxcCost": {
            "description": "Cost of VPS Slice LXC",
            "type": "number",
            "example": 6
          },
          "vpsSliceXenCost": {
            "description": "Cost of VPS Slice Xen",
            "type": "number",
            "example": 6
          },
          "vpsSliceKvmLCost": {
            "description": "Cost of VPS Slice KVM L",
            "type": "number",
            "example": 6
          },
          "vpsSliceKvmStorageCost": {
            "description": "Cost of VPS Slice KVM Storage",
            "type": "number",
            "example": 6
          },
          "vpsNyCost": {
            "description": "Cost of VPS in NY",
            "type": "number",
            "example": 3
          },
          "vpsSliceKvmWCost": {
            "description": "Cost of VPS Slice KVM Windows",
            "type": "number",
            "example": 10
          },
          "cpanelCost": {
            "description": "Cost of cPanel",
            "type": "number",
            "example": 22
          },
          "daCost": {
            "description": "Cost of DirectAdmin (DA)",
            "type": "number",
            "example": 8
          },
          "ramSlice": {
            "description": "RAM for VPS Slice",
            "type": "string",
            "example": "2048"
          },
          "hdSlice": {
            "description": "Hard Disk for VPS Slice",
            "type": "string",
            "example": "30"
          },
          "hdStorageSlice": {
            "description": "Storage Hard Disk for VPS Slice",
            "type": "string",
            "example": "1000"
          },
          "bwSlice": {
            "description": "Bandwidth for VPS Slice",
            "type": "string",
            "example": "2000"
          },
          "bwType": {
            "description": "Bandwidth Type",
            "type": "string",
            "example": "2"
          },
          "bwTotal": {
            "description": "Total Bandwidth",
            "type": "number",
            "example": 2
          },
          "maxSlices": {
            "description": "Maximum Slices",
            "type": "string",
            "example": "16"
          },
          "platformPackages": {
            "description": "Platform Packages",
            "type": "object",
            "properties": {
              "kvm": {
                "type": "number",
                "example": 32
              },
              "kvmstorage": {
                "type": "number",
                "example": 57
              },
              "hyperv": {
                "type": "number",
                "example": 54
              }
            }
          },
          "platformNames": {
            "description": "Platform Names",
            "type": "object",
            "properties": {
              "kvm": {
                "type": "string",
                "example": "KVM"
              },
              "kvmstorage": {
                "type": "string",
                "example": "KVM Storage"
              },
              "hyperv": {
                "type": "string",
                "example": "HyperV"
              }
            }
          },
          "packageCosts": {
            "description": "Package Costs",
            "type": "object",
            "properties": {
              "57": {
                "type": "number",
                "example": 6
              }
            }
          },
          "locationStock": {
            "description": "Location Stock",
            "type": "object",
            "properties": {
              "1": {
                "type": "object",
                "properties": {
                  "kvm": {
                    "type": "boolean",
                    "example": true
                  },
                  "kvmstorage": {
                    "type": "boolean",
                    "example": true
                  },
                  "hyperv": {
                    "type": "boolean",
                    "example": true
                  }
                }
              }
            }
          },
          "locationNames": {
            "description": "Location Names",
            "type": "object",
            "properties": {
              "3": {
                "type": "string",
                "example": "Equinix NY4"
              }
            }
          },
          "osNames": {
            "description": "OS Names",
            "type": "object",
            "properties": {
              "opensuse": {
                "type": "string",
                "example": "OpenSUSE"
              },
              "ubuntu": {
                "type": "string",
                "example": "Ubuntu"
              }
            }
          },
          "templates": {
            "description": "Templates",
            "type": "object",
            "properties": {
              "hyperv": {
                "type": "object",
                "properties": {
                  "windows": {
                    "type": "object",
                    "properties": {
                      "Windows2019Standard": {
                        "type": "string",
                        "example": "2019 Standard"
                      },
                      "Windows2022": {
                        "type": "string",
                        "example": "2022"
                      }
                    }
                  }
                }
              }
            }
          },
          "serviceTypes": {
            "description": "Service Types",
            "type": "object",
            "properties": {
              "32": {
                "type": "object",
                "properties": {
                  "services_id": {
                    "type": "string",
                    "example": "32"
                  },
                  "services_name": {
                    "type": "string",
                    "example": "KVM Windows VPS Slice"
                  },
                  "services_cost": {
                    "type": "string",
                    "example": "10.00"
                  },
                  "services_category": {
                    "type": "string",
                    "example": "2"
                  },
                  "services_buyable": {
                    "type": "string",
                    "example": "1"
                  },
                  "services_type": {
                    "type": "string",
                    "example": "1"
                  },
                  "services_field1": {
                    "type": "string",
                    "example": "slice"
                  },
                  "services_field2": {
                    "type": "string",
                    "example": ""
                  },
                  "services_module": {
                    "type": "string",
                    "example": "vps"
                  }
                }
              }
            }
          },
          "currency": {
            "description": "Currency",
            "type": "string",
            "example": "USD"
          },
          "currencySymbol": {
            "description": "Currency Symbol",
            "type": "string",
            "example": "$"
          }
        }
      },
      "VpsPlesk12Data": {
        "type": "object",
        "properties": {
          "admin": {
            "$ref": "#/components/schemas/VpsPleskLicense"
          },
          "pro": {
            "$ref": "#/components/schemas/VpsPleskLicense"
          },
          "host": {
            "$ref": "#/components/schemas/VpsPleskLicense"
          }
        }
      },
      "VpsPleskLicense": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "sub_name": {
            "type": "string"
          },
          "cost": {
            "type": "integer"
          }
        }
      },
      "VpsRow": {
        "title": "VpsRow",
        "description": "A result row from the `Vps` `GET` request.",
        "required": [
          "vps_status",
          "vps_name",
          "vps_ip",
          "vps_id",
          "vps_hostname",
          "vps_comment",
          "services_name",
          "repeat_invoices_cost"
        ],
        "type": "object",
        "properties": {
          "vps_id": {
            "description": "The id of the vps.",
            "type": "string",
            "example": "100"
          },
          "vps_name": {
            "description": "The name of the vps.",
            "type": "string",
            "example": "Builder"
          },
          "repeat_invoices_cost": {
            "description": "The repeat invoices cost of the vps.",
            "type": "string",
            "example": "10.00"
          },
          "vps_hostname": {
            "description": "The hostname of the vps.",
            "type": "string",
            "example": "vps100"
          },
          "vps_ip": {
            "description": "The ip of the vps.",
            "type": "string",
            "example": "64.20.46.220"
          },
          "vps_status": {
            "description": "The status of the vps.",
            "type": "string",
            "example": "active"
          },
          "services_name": {
            "description": "The services name of the vps.",
            "type": "string",
            "example": "KVM Windows VPS Slice"
          },
          "vps_comment": {
            "description": "The comment of the vps.",
            "type": "string",
            "example": ""
          }
        },
        "example": {
          "vps_id": "100",
          "vps_name": "Builder",
          "repeat_invoices_cost": "10.00",
          "vps_hostname": "vps100",
          "vps_ip": "64.20.46.220",
          "vps_status": "active",
          "services_name": "KVM Windows VPS Slice",
          "vps_comment": ""
        }
      },
      "VpsServiceAddons": {
        "description": "Add-on services and IP address information for a VPS, including control panel licenses and extra IP assignments.",
        "type": "object",
        "properties": {
          "has_cpanel": {
            "description": "Whether a cPanel license is active on this VPS.",
            "type": "boolean"
          },
          "has_directadmin": {
            "description": "Whether a DirectAdmin license is active on this VPS.",
            "type": "boolean"
          },
          "has_fantastico": {
            "description": "Whether a Fantastico license is active on this VPS.",
            "type": "boolean"
          },
          "has_softaculous": {
            "description": "Whether a Softaculous license is active on this VPS.",
            "type": "boolean"
          },
          "has_hdspace": {
            "description": "Whether extra disk space has been added to this VPS.",
            "type": "boolean"
          },
          "dedicated_ip": {
            "description": "Whether a dedicated IP address is assigned to this VPS.",
            "type": "boolean"
          },
          "extra_ips": {
            "description": "List of additional IPv4 addresses assigned to this VPS.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "extra_ips6": {
            "description": "List of additional IPv6 addresses assigned to this VPS.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "unpaid_ips": {
            "description": "List of IP addresses that have unpaid charges.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ips": {
            "description": "All IPv4 addresses assigned to this VPS.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ips6": {
            "description": "All IPv6 addresses assigned to this VPS.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cpanel_id": {
            "description": "The add-on service ID for the cPanel license.",
            "type": "integer"
          },
          "cost": {
            "description": "Total monthly add-on cost in cents.",
            "type": "integer"
          },
          "ids": {
            "description": "List of add-on service IDs active on this VPS.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rdata": {
            "description": "Raw add-on data entries.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "VpsServiceExtra": {
        "type": "object",
        "properties": {
          "spice": {
            "description": "Spice",
            "type": "integer",
            "example": 5903
          },
          "snapshots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VpsSnapshot"
            }
          }
        }
      },
      "VpsServiceInfo": {
        "type": "object",
        "properties": {
          "vps_id": {
            "description": "VPS ID",
            "type": "string",
            "example": "85872"
          },
          "vps_custid": {
            "description": "Customer ID",
            "type": "string",
            "example": "223513"
          },
          "vps_server": {
            "description": "Server ID",
            "type": "string",
            "example": "2439"
          },
          "vps_ip": {
            "description": "IP address of the VPS",
            "type": "string",
            "example": "1.2.3.4"
          },
          "vps_ipv6": {
            "description": "IPv6 address of the VPS",
            "type": "string"
          },
          "vps_vzid": {
            "description": "VPS Virtuozzo ID",
            "type": "string",
            "example": "vps85872"
          },
          "vps_currency": {
            "description": "Currency used for billing",
            "type": "string",
            "example": "USD"
          },
          "vps_type": {
            "description": "VPS type",
            "type": "string",
            "example": "33"
          },
          "vps_order_date": {
            "description": "Date of VPS order",
            "type": "string",
            "example": "2022-12-26T20:14:59.000Z"
          },
          "vps_status": {
            "description": "VPS status",
            "type": "string",
            "example": "active"
          },
          "vps_invoice": {
            "description": "VPS invoice number",
            "type": "string",
            "example": "20130799"
          },
          "vps_coupon": {
            "description": "VPS coupon code",
            "type": "string",
            "example": "3646"
          },
          "vps_extra": {
            "description": "Additional information about the VPS",
            "type": "string",
            "example": "{\"spice\":5903,\"snapshots\":[{\"name\":\"third\",\"used\":36490445,\"date\":1692095220},{\"name\":\"second\",\"used\":40894464,\"date\":1692181620},{\"name\":\"first\",\"used\":54735668,\"date\":1692268020}]}"
          },
          "vps_hostname": {
            "description": "VPS hostname",
            "type": "string",
            "example": "vps85872"
          },
          "vps_server_status": {
            "description": "Status of the VPS server",
            "type": "string",
            "example": "running"
          },
          "vps_comment": {
            "description": "Comment associated with the VPS",
            "type": "string",
            "example": "my-web-2"
          },
          "vps_slices": {
            "description": "Number of VPS slices",
            "type": "string",
            "example": "16"
          },
          "vps_vnc": {
            "description": "VNC address",
            "type": "string",
            "example": "8.7.6.5"
          },
          "vps_vnc_port": {
            "description": "VNC port",
            "type": "string",
            "example": "5902"
          },
          "vps_rootpass": {
            "description": "Root password of the VPS",
            "type": "string",
            "example": "mypassword"
          },
          "vps_mac": {
            "description": "MAC address of the VPS",
            "type": "string",
            "example": "00:16:3e:27:59:b2"
          },
          "vps_os": {
            "description": "Operating system of the VPS",
            "type": "string",
            "example": "ubuntu24"
          },
          "vps_version": {
            "description": "Version of the operating system",
            "type": "string",
            "example": "ubuntu"
          },
          "vps_location": {
            "description": "Location of the VPS",
            "type": "string",
            "example": "1"
          },
          "vps_platform": {
            "description": "Virtualization platform",
            "type": "string",
            "example": "kvm"
          },
          "vps_diskused": {
            "description": "Amount of disk space used",
            "type": "string",
            "example": "0"
          },
          "vps_diskmax": {
            "description": "Maximum disk space available",
            "type": "string",
            "example": "0"
          }
        }
      },
      "VpsServiceMaster": {
        "description": "Information about the host node (hypervisor) running this VPS, including hardware specs and resource utilization.",
        "type": "object",
        "properties": {
          "vps_id": {
            "description": "VPS ID",
            "type": "string",
            "example": "2439"
          },
          "vps_name": {
            "description": "VPS name",
            "type": "string",
            "example": "Mystaging"
          },
          "vps_ip": {
            "description": "IP address of the VPS",
            "type": "string",
            "example": "10.11.12.13"
          },
          "vps_type": {
            "description": "VPS type",
            "type": "string",
            "example": "14"
          },
          "vps_hdsize": {
            "description": "Hard drive size",
            "type": "string",
            "example": "1856"
          },
          "vps_hdfree": {
            "description": "Free hard drive space",
            "type": "string",
            "example": "1559"
          },
          "vps_bits": {
            "description": "Bits",
            "type": "string",
            "example": "64"
          },
          "vps_load": {
            "description": "CPU load",
            "type": "string",
            "example": "0.17"
          },
          "vps_ram": {
            "description": "RAM",
            "type": "string",
            "example": "263998228"
          },
          "vps_cpu_model": {
            "description": "CPU model",
            "type": "string",
            "example": "Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz"
          },
          "vps_cpu_mhz": {
            "description": "CPU frequency in MHz",
            "type": "string",
            "example": "1198.86"
          },
          "vps_location": {
            "description": "Location of the VPS",
            "type": "string",
            "example": "1"
          },
          "vps_last_update": {
            "description": "Last update date",
            "type": "string",
            "example": "2023-08-17T22:19:04.000Z"
          },
          "vps_raid_building": {
            "description": "RAID building status",
            "type": "string",
            "example": "0"
          },
          "vps_kernel": {
            "description": "Kernel version",
            "type": "string",
            "example": "5.4.0-121-generic"
          },
          "vps_available": {
            "description": "Available",
            "type": "string",
            "example": "0"
          },
          "vps_cores": {
            "description": "Number of CPU cores",
            "type": "string",
            "example": "48"
          },
          "vps_iowait": {
            "description": "I/O wait",
            "type": "string",
            "example": "0.09"
          },
          "vps_raid_status": {
            "description": "RAID status",
            "type": "string",
            "example": "OK: zfs:all pools are healthy"
          },
          "vps_mounts": {
            "description": "Mounts",
            "type": "string",
            "example": "udev:125:0:125:/dev,/dev/md1:91:53:38:/,securityfs:0:0:0:/sys/kernel/security,cgroup2:0:0:0:/sys/fs/cgroup/unified,pstore:0:0:0:/sys/fs/pstore,efivarfs:0:0:0:/sys/firmware/efi/efivars,systemd-1:0:0:0:/proc/sys/fs/binfmt_misc,hugetlbfs:0:0:0:/dev/hugepages"
          },
          "vps_server_max": {
            "description": "Maximum number of servers",
            "type": "string",
            "example": "50"
          },
          "vps_server_max_slices": {
            "description": "Maximum number of server slices",
            "type": "string",
            "example": "80"
          },
          "vps_drive_type": {
            "description": "Drive type",
            "type": "string",
            "example": "SSD"
          },
          "vps_order": {
            "description": "Order number",
            "type": "string",
            "example": "36978"
          }
        }
      },
      "VpsSnapshot": {
        "description": "A VPS snapshot (point-in-time backup) stored on the host node.",
        "type": "object",
        "properties": {
          "name": {
            "description": "Snapshot name.",
            "type": "string"
          },
          "used": {
            "description": "Disk space used by this snapshot in bytes.",
            "type": "integer"
          },
          "date": {
            "description": "Unix timestamp of when the snapshot was created.",
            "type": "integer"
          }
        }
      },
      "VpsTemplateRow": {
        "title": "VpsTemplateRow",
        "description": "A VPS OS Template.",
        "type": "object",
        "properties": {
          "template_id": {
            "type": "string"
          },
          "template_type": {
            "type": "string"
          },
          "template_os": {
            "type": "string"
          },
          "template_version": {
            "type": "string"
          },
          "template_bits": {
            "type": "string"
          },
          "template_file": {
            "type": "string"
          },
          "template_available": {
            "type": "string"
          },
          "template_name": {
            "type": "string"
          },
          "template_dir": {
            "type": "string"
          }
        },
        "example": {
          "template_id": "1906",
          "template_type": "14",
          "template_os": "ubuntu",
          "template_version": "22.04",
          "template_bits": "64",
          "template_file": "ubuntu-22.04",
          "template_available": "1",
          "template_name": "Ubuntu",
          "template_dir": ""
        }
      },
      "VpsTemplatesList": {
        "description": "A listing of the OS Templates available for use.",
        "required": [
          "templates"
        ],
        "type": "object",
        "properties": {
          "templates": {
            "description": "A listing of the templates.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VpsTemplateRow"
            }
          }
        }
      },
      "VpsTrafficDataSectionResponse": {
        "title": "VpsTrafficDataSectionResponse",
        "description": "VPS Traffic Data Section Response",
        "required": [
          "data",
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VPSTrafficDataDataSectionResponse"
            }
          }
        },
        "example": {
          "name": "74.50.91.46 In",
          "data": [
            [
              "2023-09-17T03:30:30Z",
              3968
            ],
            [
              "2023-09-17T03:31:30Z",
              840
            ],
            [
              "2023-09-17T03:32:31Z",
              632
            ],
            [
              "2023-09-17T03:33:31Z",
              1232
            ],
            [
              "2023-09-17T03:34:31Z",
              2960
            ],
            [
              "2023-09-17T07:50:12Z",
              5504
            ]
          ]
        }
      },
      "VpsTrafficHistoryResponse": {
        "title": "VpsTrafficHistoryResponse",
        "description": "VPS Traffic Data History Section",
        "required": [
          "hour",
          "day"
        ],
        "type": "object",
        "properties": {
          "hour": {
            "$ref": "#/components/schemas/VpsTrafficHistorySectionResponse"
          },
          "day": {
            "$ref": "#/components/schemas/VpsTrafficHistorySectionResponse"
          }
        },
        "example": {
          "hour": {
            "data": [
              [
                "2023-09-11T12:00:00Z",
                834424
              ],
              [
                "2023-09-11T23:00:00Z",
                8247944
              ],
              [
                "2023-09-14T05:00:00Z",
                19763064
              ],
              [
                "2023-09-14T06:00:00Z",
                18892320
              ],
              [
                "2023-09-17T07:00:00Z",
                16546448
              ]
            ],
            "times": [
              "2023-09-12T02:00:00Z",
              "2023-09-17T05:00:00Z",
              "2023-09-17T06:00:00Z",
              "2023-09-17T07:00:00Z"
            ]
          },
          "day": {
            "data": [
              [
                "2023-09-12T00:00:00Z",
                1455497448
              ],
              [
                "2023-09-13T00:00:00Z",
                182943992
              ],
              [
                "2023-09-14T00:00:00Z",
                290416056
              ],
              [
                "2023-09-15T00:00:00Z",
                440595840
              ],
              [
                "2023-09-16T00:00:00Z",
                411255120
              ],
              [
                "2023-09-17T00:00:00Z",
                346449520
              ]
            ],
            "times": [
              "2023-09-12T00:00:00Z",
              "2023-09-13T00:00:00Z",
              "2023-09-14T00:00:00Z",
              "2023-09-15T00:00:00Z",
              "2023-09-16T00:00:00Z",
              "2023-09-17T00:00:00Z"
            ]
          }
        }
      },
      "VpsTrafficHistorySectionDataResponse": {
        "description": "VPS Traffic Hisotrty Data Row of Hour/Day Section",
        "type": "array",
        "items": {
          "oneOf": [
            {
              "format": "date-time",
              "type": "string"
            },
            {
              "type": "integer"
            }
          ]
        }
      },
      "VpsTrafficHistorySectionResponse": {
        "title": "VpsTrafficHistorySectionResponse",
        "description": "VPS Traffic History Hour and Day Sections",
        "required": [
          "data",
          "times"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VpsTrafficHistorySectionDataResponse"
            }
          },
          "times": {
            "type": "array",
            "items": {
              "format": "date-time",
              "type": "string"
            }
          }
        },
        "example": {
          "data": [
            [
              "2023-09-11T12:00:00Z",
              834424
            ],
            [
              "2023-09-11T23:00:00Z",
              8247944
            ],
            [
              "2023-09-14T05:00:00Z",
              19763064
            ],
            [
              "2023-09-14T06:00:00Z",
              18892320
            ],
            [
              "2023-09-17T07:00:00Z",
              16546448
            ]
          ],
          "times": [
            "2023-09-12T02:00:00Z",
            "2023-09-17T05:00:00Z",
            "2023-09-17T06:00:00Z",
            "2023-09-17T07:00:00Z"
          ]
        }
      },
      "VpsTrafficResponse": {
        "title": "VpsTrafficResponse",
        "description": "VPS Traffic Information",
        "required": [
          "history",
          "usage",
          "totals",
          "times",
          "target",
          "name",
          "last",
          "interval",
          "data"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "target": {
            "type": "string"
          },
          "interval": {
            "format": "int32",
            "type": "integer"
          },
          "history": {
            "$ref": "#/components/schemas/VpsTrafficHistoryResponse"
          },
          "last": {
            "format": "date-time",
            "type": "string"
          },
          "times": {
            "type": "array",
            "items": {
              "format": "date-time",
              "type": "string"
            }
          },
          "totals": {
            "$ref": "#/components/schemas/VpsTrafficTotalsResposne"
          },
          "usage": {
            "$ref": "#/components/schemas/VpsTrafficUsageResponse"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VpsTrafficDataSectionResponse"
            }
          }
        },
        "example": {
          "name": "vps2725349",
          "target": "2725349",
          "interval": 30,
          "history": {
            "hour": {
              "data": [
                [
                  "2023-09-11T12:00:00Z",
                  834424
                ],
                [
                  "2023-09-11T23:00:00Z",
                  8247944
                ],
                [
                  "2023-09-14T05:00:00Z",
                  19763064
                ],
                [
                  "2023-09-14T06:00:00Z",
                  18892320
                ],
                [
                  "2023-09-17T07:00:00Z",
                  16546448
                ]
              ],
              "times": [
                "2023-09-12T02:00:00Z",
                "2023-09-17T05:00:00Z",
                "2023-09-17T06:00:00Z",
                "2023-09-17T07:00:00Z"
              ]
            },
            "day": {
              "data": [
                [
                  "2023-09-12T00:00:00Z",
                  1455497448
                ],
                [
                  "2023-09-13T00:00:00Z",
                  182943992
                ],
                [
                  "2023-09-14T00:00:00Z",
                  290416056
                ],
                [
                  "2023-09-15T00:00:00Z",
                  440595840
                ],
                [
                  "2023-09-16T00:00:00Z",
                  411255120
                ],
                [
                  "2023-09-17T00:00:00Z",
                  346449520
                ]
              ],
              "times": [
                "2023-09-12T00:00:00Z",
                "2023-09-13T00:00:00Z",
                "2023-09-14T00:00:00Z",
                "2023-09-15T00:00:00Z",
                "2023-09-16T00:00:00Z",
                "2023-09-17T00:00:00Z"
              ]
            }
          },
          "last": "2023-09-17T07:50:12Z",
          "times": [
            "2023-09-17T03:30:30Z",
            "2023-09-17T03:31:30Z",
            "2023-09-17T03:32:31Z",
            "2023-09-17T03:33:31Z",
            "2023-09-17T04:12:30Z",
            "2023-09-17T04:13:31Z",
            "2023-09-17T04:14:30Z",
            "2023-09-17T04:15:30Z",
            "2023-09-17T04:16:31Z",
            "2023-09-17T04:17:30Z",
            "2023-09-17T04:18:31Z",
            "2023-09-17T04:19:30Z",
            "2023-09-17T04:20:30Z",
            "2023-09-17T04:21:30Z",
            "2023-09-17T04:22:31Z",
            "2023-09-17T07:50:12Z"
          ],
          "totals": {
            "day": {
              "in": 49719744,
              "out": 11316104
            },
            "month": {
              "in": 2880512936,
              "out": 380333384
            },
            "year": {
              "in": 2880512936,
              "out": 380333384
            },
            "all": {
              "in": 2880512936,
              "out": 380333384
            }
          },
          "usage": {
            "current": {
              "in": 5504,
              "out": 1248
            },
            "peak": {
              "in": 9176,
              "out": 3600
            },
            "average": {
              "in": {
                "total": 949688,
                "count": 261,
                "value": 3639
              },
              "out": {
                "total": 200752,
                "count": 261,
                "value": 770
              }
            }
          },
          "data": [
            {
              "name": "74.50.91.46 In",
              "data": [
                [
                  "2023-09-17T03:30:30Z",
                  3968
                ],
                [
                  "2023-09-17T03:31:30Z",
                  840
                ],
                [
                  "2023-09-17T03:32:31Z",
                  632
                ],
                [
                  "2023-09-17T03:33:31Z",
                  1232
                ],
                [
                  "2023-09-17T03:34:31Z",
                  2960
                ],
                [
                  "2023-09-17T07:50:12Z",
                  5504
                ]
              ]
            },
            {
              "name": "74.50.91.46 Out",
              "data": [
                [
                  "2023-09-17T03:30:30Z",
                  456
                ],
                [
                  "2023-09-17T03:31:30Z",
                  472
                ],
                [
                  "2023-09-17T03:48:30Z",
                  592
                ]
              ]
            }
          ]
        }
      },
      "VpsTrafficTotalsResposne": {
        "title": "VpsTrafficTotalsResposne",
        "description": "VPS Traffic Totals Section",
        "required": [
          "month",
          "day",
          "all",
          "year"
        ],
        "type": "object",
        "properties": {
          "day": {
            "$ref": "#/components/schemas/VpsTrafficTotalsSectionResponse"
          },
          "month": {
            "$ref": "#/components/schemas/VpsTrafficTotalsSectionResponse"
          },
          "year": {
            "$ref": "#/components/schemas/VpsTrafficTotalsSectionResponse"
          },
          "all": {
            "$ref": "#/components/schemas/VpsTrafficTotalsSectionResponse"
          }
        },
        "example": {
          "day": {
            "in": 49719744,
            "out": 11316104
          },
          "month": {
            "in": 2880512936,
            "out": 380333384
          },
          "year": {
            "in": 2880512936,
            "out": 380333384
          },
          "all": {
            "in": 2880512936,
            "out": 380333384
          }
        }
      },
      "VpsTrafficTotalsSectionResponse": {
        "title": "VpsTrafficTotalsSectionResponse",
        "description": "VPS Traffic Totals Secttions Data",
        "required": [
          "out",
          "in"
        ],
        "type": "object",
        "properties": {
          "in": {
            "format": "int64",
            "type": "integer"
          },
          "out": {
            "format": "int64",
            "type": "integer"
          }
        },
        "example": {
          "in": 49719744,
          "out": 11316104
        }
      },
      "VpsTrafficUsageAverageResponse": {
        "title": "VpsTrafficUsageAverageResponse",
        "description": "VPS Traffic Usage Average Section",
        "required": [
          "out",
          "in"
        ],
        "type": "object",
        "properties": {
          "in": {
            "$ref": "#/components/schemas/VpsTrafficUsageAverageSectionResponse"
          },
          "out": {
            "$ref": "#/components/schemas/VpsTrafficUsageAverageSectionResponse"
          }
        },
        "example": {
          "in": {
            "total": 949688,
            "count": 261,
            "value": 3639
          },
          "out": {
            "total": 200752,
            "count": 261,
            "value": 770
          }
        }
      },
      "VpsTrafficUsageAverageSectionResponse": {
        "title": "VpsTrafficUsageAverageSectionResponse",
        "description": "VPS Traffic Usage Average Section Response",
        "required": [
          "value",
          "total",
          "count"
        ],
        "type": "object",
        "properties": {
          "total": {
            "format": "int32",
            "type": "integer"
          },
          "count": {
            "format": "int32",
            "type": "integer"
          },
          "value": {
            "format": "int32",
            "type": "integer"
          }
        },
        "example": {
          "total": 949688,
          "count": 261,
          "value": 3639
        }
      },
      "VpsTrafficUsageResponse": {
        "title": "VpsTrafficUsageResponse",
        "description": "VPS Traffic Usage Section",
        "required": [
          "average",
          "current",
          "peak"
        ],
        "type": "object",
        "properties": {
          "current": {
            "$ref": "#/components/schemas/VpsTrafficTotalsSectionResponse"
          },
          "peak": {
            "$ref": "#/components/schemas/VpsTrafficTotalsSectionResponse"
          },
          "average": {
            "$ref": "#/components/schemas/VpsTrafficUsageAverageResponse"
          }
        },
        "example": {
          "current": {
            "in": 5504,
            "out": 1248
          },
          "peak": {
            "in": 9176,
            "out": 3600
          },
          "average": {
            "in": {
              "total": 949688,
              "count": 261,
              "value": 3639
            },
            "out": {
              "total": 200752,
              "count": 261,
              "value": 770
            }
          }
        }
      },
      "Website": {
        "description": "Full detail view of a webhosting service including billing, service configuration, and panel access links.",
        "type": "object",
        "properties": {
          "serviceInfo": {
            "$ref": "#/components/schemas/WebsiteServiceInfo"
          },
          "client_links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebsiteClientLink"
            }
          },
          "billingDetails": {
            "$ref": "#/components/schemas/WebsiteBillingDetails"
          },
          "custCurrency": {
            "description": "Customer's currency",
            "type": "string",
            "example": "USD"
          },
          "custCurrencySymbol": {
            "description": "Customer currency symbol",
            "type": "string",
            "example": "$"
          },
          "serviceMaster": {
            "$ref": "#/components/schemas/WebsiteServiceMaster"
          },
          "package": {
            "description": "Package information",
            "type": "string",
            "example": "Web Hosting Direct Admin"
          },
          "serviceExtra": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebsiteServiceExtra"
            }
          },
          "extraInfoTables": {
            "$ref": "#/components/schemas/WebsiteExtraInfoTables"
          }
        }
      },
      "WebsiteBillingDetails": {
        "description": "Billing information for a webhosting service including payment status, billing cycle, and cost.",
        "type": "object",
        "properties": {
          "service_last_invoice_date": {
            "description": "Last invoice date for the service",
            "type": "string"
          },
          "service_payment_status": {
            "description": "Payment status for the service",
            "type": "string"
          },
          "service_frequency": {
            "description": "Frequency of the service",
            "type": "string"
          },
          "next_date": {
            "description": "Next date for the service",
            "type": "string"
          },
          "service_next_invoice_date": {
            "description": "Next invoice date for the service",
            "type": "string"
          },
          "service_currency": {
            "description": "Currency for the service",
            "type": "string",
            "example": "USD"
          },
          "service_currency_symbol": {
            "description": "Currency symbol for the service",
            "type": "string",
            "example": "$"
          },
          "service_coupon": {
            "description": "Coupon for the service",
            "type": "string"
          },
          "service_cost_info": {
            "description": "Cost information for the service",
            "type": "string"
          },
          "service_extra": {
            "$ref": "#/components/schemas/WebsiteServiceExtra"
          },
          "service_extra_json": {
            "description": "Extra information in JSON format for the service",
            "type": "string"
          }
        }
      },
      "WebsiteClientLink": {
        "description": "A navigation link for webhosting-related actions in the client portal.",
        "type": "object",
        "properties": {
          "label": {
            "description": "Label for the link",
            "type": "string"
          },
          "link": {
            "description": "Link URL",
            "type": "string"
          },
          "icon": {
            "description": "Icon for the link",
            "type": "string"
          },
          "icon_text": {
            "description": "Icon text for the link",
            "type": "string"
          },
          "help_text": {
            "description": "Help text for the link",
            "type": "string"
          },
          "other_attr": {
            "description": "Other attributes for the link",
            "type": "string"
          }
        }
      },
      "WebsiteExtraInfoTables": {
        "description": "Supplementary information tables displayed for a webhosting service (links, DNS, preview).",
        "type": "object",
        "properties": {
          "links": {
            "$ref": "#/components/schemas/WebsiteTable"
          },
          "preview": {
            "$ref": "#/components/schemas/WebsiteTable"
          },
          "dns": {
            "$ref": "#/components/schemas/WebsiteTable"
          }
        }
      },
      "WebsiteLoginResponse": {
        "title": "WebsiteLoginResponse",
        "description": "Response from a website login request.",
        "type": "object",
        "properties": {
          "type": {
            "type": "string"
          },
          "location": {
            "type": "string"
          }
        },
        "example": {
          "type": "location",
          "location": "https://www.site.com/"
        }
      },
      "WebsiteRow": {
        "title": "WebsiteRow",
        "description": "A result row from the `Webhosting` `GET` request.",
        "required": [
          "website_status",
          "website_id",
          "website_hostname",
          "website_comment",
          "services_name",
          "repeat_invoices_cost"
        ],
        "type": "object",
        "properties": {
          "website_id": {
            "description": "The id of the website.",
            "type": "string",
            "example": "376359"
          },
          "website_hostname": {
            "description": "The hostname of the website.",
            "type": "string",
            "example": "hussfamily.com"
          },
          "repeat_invoices_cost": {
            "description": "The repeat invoices cost of the website.",
            "type": "string",
            "example": "5.99"
          },
          "website_status": {
            "description": "The status of the website.",
            "type": "string",
            "example": "active"
          },
          "services_name": {
            "description": "The services name of the website.",
            "type": "string",
            "example": "Standard Web Hosting"
          },
          "website_comment": {
            "description": "The comment of the website.",
            "type": "string",
            "example": "website for manhattan project."
          }
        },
        "example": {
          "website_id": "376359",
          "website_hostname": "hussfamily.com",
          "repeat_invoices_cost": "5.99",
          "website_status": "active",
          "services_name": "Standard Web Hosting",
          "website_comment": ""
        }
      },
      "WebsiteServiceExtra": {
        "description": "Extra information for the service",
        "type": "object"
      },
      "WebsiteServiceInfo": {
        "type": "object",
        "properties": {
          "website_id": {
            "description": "Website ID",
            "type": "string",
            "example": "1196829"
          },
          "website_server": {
            "description": "Website server",
            "type": "string",
            "example": "543"
          },
          "website_type": {
            "description": "Website type",
            "type": "string",
            "example": "11363"
          },
          "website_currency": {
            "description": "Currency of the website",
            "type": "string",
            "example": "USD"
          },
          "website_order_date": {
            "description": "Order date of the website",
            "type": "string",
            "example": "2023-03-16T22:51:54.000Z"
          },
          "website_custid": {
            "description": "Customer ID of the website",
            "type": "string",
            "example": "85872"
          },
          "website_ip": {
            "description": "IP address of the website",
            "type": "string",
            "example": "74.50.80.15"
          },
          "website_status": {
            "description": "Status of the website",
            "type": "string",
            "example": "active"
          },
          "website_invoice": {
            "description": "Invoice of the website",
            "type": "string",
            "example": "20261994"
          },
          "website_coupon": {
            "description": "Coupon for the website",
            "type": "string",
            "example": "1690"
          },
          "website_extra": {
            "description": "Extra information in JSON format for the website",
            "type": "string",
            "example": "[]"
          },
          "website_hostname": {
            "description": "Hostname of the website",
            "type": "string",
            "example": "vintagevultures.com"
          },
          "website_comment": {
            "description": "Comment for the website",
            "type": "string"
          },
          "website_username": {
            "description": "Username for the website",
            "type": "string",
            "example": "vintagev"
          },
          "website_server_status": {
            "description": "Server status of the website",
            "type": "string"
          }
        }
      },
      "WebsiteServiceMaster": {
        "type": "object",
        "properties": {
          "website_id": {
            "description": "Website ID for the service master",
            "type": "string",
            "example": "543"
          },
          "website_name": {
            "description": "Website name for the service master",
            "type": "string",
            "example": "vda4200.is.cc"
          },
          "website_ip": {
            "description": "IP address for the service master",
            "type": "string",
            "example": "74.50.80.15"
          },
          "website_type": {
            "description": "Website type for the service master",
            "type": "string",
            "example": "206"
          },
          "website_available": {
            "description": "Availability status for the service master",
            "type": "string",
            "example": "0"
          },
          "website_hdsize": {
            "description": "Hard drive size for the service master",
            "type": "string",
            "example": "2062"
          },
          "website_hdfree": {
            "description": "Free hard drive space for the service master",
            "type": "string",
            "example": "196"
          },
          "website_load": {
            "description": "Load for the service master",
            "type": "string",
            "example": "6.55"
          },
          "website_last_update": {
            "description": "Last update date for the service master",
            "type": "string",
            "example": "2023-08-17T23:01:02.000Z"
          },
          "website_max_sites": {
            "description": "Maximum number of sites for the service master",
            "type": "string",
            "example": "300"
          },
          "website_order": {
            "description": "Order number for the service master",
            "type": "string",
            "example": "58984"
          },
          "website_partitions": {
            "description": "Partitions for the service master",
            "type": "string"
          },
          "website_dns1": {
            "description": "DNS server 1 for the service master",
            "type": "string",
            "example": "vda4200a.trouble-free.net"
          },
          "website_dns2": {
            "description": "DNS server 2 for the service master",
            "type": "string",
            "example": "vda4200b.trouble-free.net"
          }
        }
      },
      "WebsiteTable": {
        "type": "object",
        "properties": {
          "title": {
            "description": "Title of the table",
            "type": "string"
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebsiteTableRow"
            }
          }
        }
      },
      "WebsiteTableRow": {
        "type": "object",
        "properties": {
          "desc": {
            "description": "Description for the row",
            "type": "string"
          },
          "value": {
            "description": "Value for the row",
            "type": "string"
          }
        }
      },
      "WebsitesOrder": {
        "description": "Schema for the WebsitesOrder object",
        "required": [
          "serviceTypes",
          "serviceOffers",
          "packages",
          "step",
          "website",
          "period",
          "serviceOfferId",
          "enableDomainRegistering",
          "jsonServices",
          "jsonServiceOffers"
        ],
        "type": "object",
        "properties": {
          "step": {
            "description": "Step description",
            "type": "string",
            "example": "order_form"
          },
          "website": {
            "description": "Website description",
            "type": "string",
            "example": ""
          },
          "period": {
            "description": "Period description",
            "type": "integer",
            "example": 1
          },
          "serviceOfferId": {
            "description": "Service offer ID description",
            "type": "integer",
            "example": 0
          },
          "packages": {
            "$ref": "#/components/schemas/WebsitesOrderPackages"
          },
          "enableDomainRegistering": {
            "description": "Enable domain registering description",
            "type": "boolean",
            "example": false
          },
          "jsonServices": {
            "$ref": "#/components/schemas/WebsitesOrderJsonServices"
          },
          "jsonServiceOffers": {
            "$ref": "#/components/schemas/WebsitesOrderJsonServiceOffers"
          },
          "serviceTypes": {
            "description": "The service types data.",
            "required": [
              "11447"
            ],
            "type": "object",
            "properties": {
              "11447": {
                "$ref": "#/components/schemas/WebsitesOrderServiceTypes"
              }
            }
          },
          "serviceOffers": {
            "description": "The service offers data.",
            "required": [
              "1026"
            ],
            "type": "object",
            "properties": {
              "1026": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/WebsitesOrderServiceOffer"
                }
              }
            }
          },
          "packges": {
            "description": "The packages data.",
            "required": [
              "11440"
            ],
            "type": "object",
            "properties": {
              "11440": {
                "$ref": "#/components/schemas/WebsitesOrderPackagesInfo"
              }
            }
          }
        }
      },
      "WebsitesOrderJsonServiceOffers": {
        "description": "Schema for the jsonServiceOffers field in WebsitesOrder",
        "required": [
          "1026"
        ],
        "type": "object",
        "properties": {
          "1026": {
            "description": "Array of service offers",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebsitesOrderJsonServiceOffersItem"
            }
          }
        }
      },
      "WebsitesOrderJsonServiceOffersItem": {
        "type": "object",
        "properties": {
          "service_offer_id": {
            "description": "Service offer ID",
            "type": "string",
            "example": "136"
          },
          "service_id": {
            "description": "Service ID",
            "type": "string",
            "example": "1026"
          },
          "intro_cost": {
            "description": "Introductory cost",
            "type": "integer",
            "example": 96
          },
          "renewal_cost": {
            "description": "Renewal cost",
            "type": "integer",
            "example": 96
          },
          "intro_frequency": {
            "description": "Introductory frequency",
            "type": "string",
            "example": "12"
          },
          "renewal_frequency": {
            "description": "Renewal frequency",
            "type": "string",
            "example": "12"
          },
          "allow_coupon": {
            "description": "Allow coupon",
            "type": "string",
            "example": "1"
          },
          "service_module": {
            "description": "Service module",
            "type": "string",
            "example": "webhosting"
          },
          "created_at": {
            "description": "Creation date",
            "type": "string",
            "example": "2023-01-25T14:50:55.000Z"
          },
          "updated_at": {
            "description": "Update date",
            "type": "string"
          },
          "deleted_at": {
            "description": "Deletion date",
            "type": "string"
          },
          "currencySymbol": {
            "description": "Currency symbol",
            "type": "string",
            "example": "$"
          }
        }
      },
      "WebsitesOrderJsonServices": {
        "description": "Schema for the jsonServices field in WebsitesOrder",
        "required": [
          "11447"
        ],
        "type": "object",
        "properties": {
          "11447": {
            "description": "JSON services description",
            "type": "string",
            "example": "5.00"
          }
        }
      },
      "WebsitesOrderPackages": {
        "description": "Schema for the packages field in WebsitesOrder",
        "required": [
          "11447"
        ],
        "type": "object",
        "properties": {
          "11447": {
            "description": "Package description",
            "type": "string",
            "example": "Web Hosting Direct Admin (PriceLock)"
          }
        }
      },
      "WebsitesOrderPackagesInfo": {
        "required": [
          "services_id",
          "services_name",
          "services_cost",
          "services_category",
          "services_buyable",
          "services_type",
          "services_module",
          "services_description"
        ],
        "type": "object",
        "properties": {
          "services_id": {
            "description": "The ID of the package.",
            "type": "string",
            "example": "11440"
          },
          "services_name": {
            "description": "The name of the package.",
            "type": "string",
            "example": "DA BOOST X"
          },
          "services_cost": {
            "description": "The cost of the package.",
            "type": "string",
            "example": "69.95"
          },
          "services_category": {
            "description": "The category of the package.",
            "type": "string",
            "example": "204"
          },
          "services_buyable": {
            "description": "Indicates if the package is buyable (1 for yes, 0 for no).",
            "type": "string",
            "example": "1"
          },
          "services_type": {
            "description": "The type of the package.",
            "type": "string",
            "example": "206"
          },
          "services_field1": {
            "description": "Additional field 1 for the package.",
            "type": "string",
            "example": ""
          },
          "services_field2": {
            "description": "Additional field 2 for the package.",
            "type": "string",
            "example": "BoostX"
          },
          "services_module": {
            "description": "The module of the package.",
            "type": "string",
            "example": "webhosting"
          },
          "services_html": {
            "description": "HTML content for the package.",
            "type": "string",
            "example": ""
          },
          "services_description": {
            "description": "Description of the package.",
            "type": "string",
            "example": "Direct Admin Web hosting package with 10x more resources over our standard web hosting package."
          },
          "services_moreinfo_url": {
            "description": "URL for more information about the package.",
            "type": "string",
            "example": ""
          },
          "services_hidden": {
            "description": "Indicates if the package is hidden (1 for yes, 0 for no).",
            "type": "string",
            "example": "0"
          }
        }
      },
      "WebsitesOrderServiceOffer": {
        "required": [
          "service_offer_id",
          "service_id",
          "intro_cost",
          "renewal_cost",
          "intro_frequency",
          "renewal_frequency",
          "allow_coupon",
          "service_module",
          "created_at"
        ],
        "type": "object",
        "properties": {
          "service_offer_id": {
            "description": "The ID of the service offer.",
            "type": "string",
            "example": "136"
          },
          "service_id": {
            "description": "The ID of the associated service.",
            "type": "string",
            "example": "1026"
          },
          "intro_cost": {
            "description": "The introductory cost of the service offer.",
            "type": "string",
            "example": "96.00"
          },
          "renewal_cost": {
            "description": "The renewal cost of the service offer.",
            "type": "string",
            "example": "96.00"
          },
          "intro_frequency": {
            "description": "The introductory frequency of the service offer.",
            "type": "string",
            "example": "12"
          },
          "renewal_frequency": {
            "description": "The renewal frequency of the service offer.",
            "type": "string",
            "example": "12"
          },
          "allow_coupon": {
            "description": "Indicates if coupons are allowed (1 for yes, 0 for no).",
            "type": "string",
            "example": "1"
          },
          "service_module": {
            "description": "The module of the service offer.",
            "type": "string",
            "example": "webhosting"
          },
          "created_at": {
            "description": "The creation timestamp of the service offer.",
            "type": "string",
            "example": "2023-01-25T14:50:55.000Z"
          },
          "updated_at": {
            "description": "The update timestamp of the service offer.",
            "type": "string"
          },
          "deleted_at": {
            "description": "The deletion timestamp of the service offer.",
            "type": "string"
          }
        }
      },
      "WebsitesOrderServiceTypes": {
        "required": [
          "services_id",
          "services_name",
          "services_cost",
          "services_category",
          "services_buyable",
          "services_type",
          "services_module",
          "services_hidden"
        ],
        "type": "object",
        "properties": {
          "services_id": {
            "description": "The ID of the service.",
            "type": "string",
            "example": "11447"
          },
          "services_name": {
            "description": "The name of the service.",
            "type": "string",
            "example": "Web Hosting Direct Admin (PriceLock)"
          },
          "services_cost": {
            "description": "The cost of the service.",
            "type": "string",
            "example": "5.00"
          },
          "services_category": {
            "description": "The category of the service.",
            "type": "string",
            "example": "204"
          },
          "services_buyable": {
            "description": "Indicates if the service is buyable (1 for yes, 0 for no).",
            "type": "string",
            "example": "1"
          },
          "services_type": {
            "description": "The type of the service.",
            "type": "string",
            "example": "206"
          },
          "services_field1": {
            "description": "Additional field 1 for the service.",
            "type": "string",
            "example": ""
          },
          "services_field2": {
            "description": "Additional field 2 for the service.",
            "type": "string",
            "example": "Standard"
          },
          "services_module": {
            "description": "The module of the service.",
            "type": "string",
            "example": "webhosting"
          },
          "services_html": {
            "description": "HTML content for the service.",
            "type": "string"
          },
          "services_description": {
            "description": "Description of the service.",
            "type": "string"
          },
          "services_moreinfo_url": {
            "description": "URL for more information about the service.",
            "type": "string"
          },
          "services_hidden": {
            "description": "Indicates if the service is hidden (1 for yes, 0 for no).",
            "type": "string",
            "example": "1"
          }
        }
      },
      "IpObject": {
        "title": "IpObject",
        "description": "IP Address",
        "required": [
          "ip"
        ],
        "type": "object",
        "properties": {
          "ip": {
            "description": "IP Address",
            "type": "string"
          }
        },
        "example": {
          "ip": "1.2.3.4"
        }
      },
      "LoginSuccessResponse": {
        "title": "LoginSuccessResponse",
        "description": "The response from a successful login.",
        "type": "object",
        "properties": {
          "sessionId": {
            "type": "string"
          },
          "account_id": {
            "format": "int32",
            "type": "integer"
          },
          "account_lid": {
            "type": "string"
          },
          "ima": {
            "type": "string"
          },
          "gravatar": {
            "type": "string"
          }
        },
        "example": {
          "sessionId": "zzzzzzzzzzzzzz",
          "account_id": 123,
          "account_lid": "my@user.com",
          "ima": "client",
          "gravatar": "https://gravatar.com/user/image.png"
        }
      },
      "EmailAddressName": {
        "title": "EmailAddressName",
        "description": "An email contact.",
        "required": [
          "email"
        ],
        "type": "object",
        "properties": {
          "email": {
            "description": "The email address.",
            "type": "string",
            "example": "user@domain.com"
          },
          "name": {
            "description": "Name to use for the sending contact.",
            "type": "string",
            "example": "John Smith"
          }
        },
        "example": {
          "email": "user@domain.com",
          "name": "John Smith"
        }
      },
      "EmailAddress": {
        "title": "EmailAddress",
        "description": "an email address",
        "type": "object",
        "properties": {
          "email": {
            "description": "an email address",
            "type": "string",
            "example": "user@domain.com"
          }
        },
        "example": {
          "email": "user@domain.com"
        }
      },
      "DenyRuleRecord": {
        "title": "DenyRuleRecord",
        "description": "The data for a email deny rule record.",
        "type": "object",
        "allOf": [
          {
            "required": [
              "id",
              "created"
            ],
            "type": "object",
            "properties": {
              "id": {
                "description": "The deny rule Id number.",
                "type": "integer",
                "example": 41124
              },
              "created": {
                "description": "the date the rule was created.",
                "type": "string",
                "example": "2022-03-22 19:16:35"
              }
            }
          },
          {
            "$ref": "#/components/schemas/DenyRuleNew"
          }
        ],
        "example": {
          "id": 14,
          "user": "mb20682",
          "type": "email",
          "data": "domeinwo@server.guesshost.net",
          "created": "2022-03-22 19:16:35"
        }
      },
      "DenyRuleNew": {
        "title": "DenyRuleNew",
        "description": "The data for a email deny rule record.",
        "required": [
          "type",
          "data"
        ],
        "type": "object",
        "properties": {
          "user": {
            "description": "Mail account username that will be tied to this rule.  If not specified the first active mail order will be used.",
            "type": "string",
            "example": "mb20682"
          },
          "type": {
            "description": "The type of deny rule.",
            "enum": [
              "domain",
              "email",
              "startswith",
              "destination"
            ],
            "type": "string",
            "example": "email"
          },
          "data": {
            "description": "The content of the rule.  If a domain type rule then an example would be google.com. For a begins with type an example would be msgid-.  For the email typer an example would be user@server.com.",
            "type": "string",
            "example": "domeinwo@server.guesshost.net"
          }
        },
        "example": {
          "user": "mb20682",
          "type": "email",
          "data": "domeinwo@server.guesshost.net"
        }
      },
      "MailAttachment": {
        "title": "MailAttachment",
        "description": "(optional) File attachments to include in the email.  The file contents must be base64",
        "required": [
          "data",
          "filename"
        ],
        "type": "object",
        "properties": {
          "filename": {
            "description": "The filename of the attached file.",
            "type": "string",
            "example": "message.txt"
          },
          "data": {
            "description": "The file contents base64 encoded",
            "type": "string",
            "example": "aGVsbG8gdGhlcmUK"
          }
        },
        "example": {
          "filename": "file.txt",
          "data": "base64_encoded_file_contents"
        }
      },
      "MailBlockClickHouse": {
        "title": "MailBlockClickHouse",
        "description": "A block entry from the clickhouse mailblocks server.",
        "required": [
          "date",
          "to",
          "subject",
          "messageId",
          "from"
        ],
        "type": "object",
        "properties": {
          "date": {
            "format": "date",
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "to": {
            "type": "string"
          }
        },
        "example": {
          "date": "2023-08-07",
          "from": "user@domain.com",
          "messageId": "pFaRqFUEWkucjhTuIzYuoAgWU@domain.com",
          "subject": "Test Email",
          "to": "['client@site.com']"
        }
      },
      "MailBlockRspamd": {
        "title": "MailBlockRspamd",
        "description": "This is a block entry from the rspamd block list.",
        "required": [
          "subject",
          "from"
        ],
        "type": "object",
        "properties": {
          "from": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          }
        },
        "example": {
          "from": "user@domain.com",
          "subject": "Test email"
        }
      },
      "MailBlocks": {
        "title": "MailBlocks",
        "description": "The listing of blocked emails.",
        "required": [
          "local",
          "mbtrap",
          "subject"
        ],
        "type": "object",
        "properties": {
          "local": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailBlockClickHouse"
            }
          },
          "mbtrap": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailBlockClickHouse"
            }
          },
          "subject": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailBlockRspamd"
            }
          }
        },
        "example": {
          "local": [
            {
              "date": "2023-08-07",
              "from": "user@domain.com",
              "messageId": "pFaRqFUEWkucjhTuIzYuoAgWU@domain.com",
              "subject": "Test Email",
              "to": "['client@site.com']"
            }
          ],
          "mbtrap": [
            {
              "date": "2023-08-07",
              "from": "user@domain.com",
              "messageId": "pFaRqFUEWkucjhTuIzYuoAgWU@domain.com",
              "subject": "Test Email",
              "to": "['client@site.com']"
            }
          ],
          "subject": [
            {
              "from": "user@domain.com",
              "subject": "Test Email"
            }
          ]
        }
      },
      "MailLog": {
        "title": "MailLog",
        "description": "Paginated mail log response.  Contains the full matched count (`total`) plus a page of `MailLogEntry` records.  The `total` reflects the grouping mode: with `groupby=recipient` it counts delivery attempts, with `groupby=message` it counts unique messages.",
        "required": [
          "total",
          "skip",
          "limit",
          "emails"
        ],
        "type": "object",
        "properties": {
          "total": {
            "description": "Total number of log entries that match the supplied filters, regardless of `skip` and `limit`.  Reflects the `groupby` mode.",
            "type": "integer",
            "example": 10234
          },
          "skip": {
            "description": "The `skip` value used for this page (echoed from the request).",
            "type": "integer",
            "example": 0
          },
          "limit": {
            "description": "The `limit` value used for this page (echoed from the request).",
            "type": "integer",
            "example": 100
          },
          "emails": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailLogEntry"
            },
            "example": [
              {
                "_id": 103172,
                "id": "17c7eda538e0005d03",
                "from": "person@mysite.com",
                "to": "client@isp.com",
                "subject": "sell 0.005 shares",
                "messageId": "<vmiLEebsuCbSpUxD7oN3REpaN4VbN6BrdCAbNKIrdAo@relay0.mailbaby.net>",
                "created": "2021-10-14 08:50:10",
                "time": 1634215809,
                "user": "mb5658",
                "transtype": "ESMTPSA",
                "origin": "199.231.189.154",
                "interface": "feeder",
                "sendingZone": "interserver",
                "bodySize": 63,
                "seq": 1,
                "delivered": 1,
                "code": 250,
                "recipient": "client@isp.com",
                "domain": "interserver.net",
                "locked": 1,
                "lockTime": "1634215818533",
                "assigned": "relay1",
                "queued": "2021-10-14T12:50:15.487Z",
                "mxHostname": "mx.j.is.cc",
                "response": "250 2.0.0 Ok queued as C91D83E128C"
              }
            ]
          }
        },
        "example": {
          "total": 1,
          "skip": 0,
          "limit": 100,
          "emails": [
            {
              "_id": 103172,
              "id": "17c7eda538e0005d03",
              "from": "person@mysite.com",
              "to": "client@isp.com",
              "subject": "sell 0.005 shares",
              "messageId": "<vmiLEebsuCbSpUxD7oN3REpaN4VbN6BrdCAbNKIrdAo@relay0.mailbaby.net>",
              "created": "2021-10-14 08:50:10",
              "time": 1634215809,
              "user": "mb5658",
              "transtype": "ESMTPSA",
              "origin": "199.231.189.154",
              "interface": "feeder",
              "sendingZone": "interserver",
              "bodySize": 63,
              "seq": 1,
              "delivered": 1,
              "code": 250,
              "recipient": "client@isp.com",
              "domain": "interserver.net",
              "locked": 1,
              "lockTime": "1634215818533",
              "assigned": "relay1",
              "queued": "2021-10-14T12:50:15.487Z",
              "mxHostname": "mx.j.is.cc",
              "response": "250 2.0.0 Ok queued as C91D83E128C"
            }
          ]
        }
      },
      "MailLogEntry": {
        "title": "MailLogEntry",
        "description": "A single email record in the mail log.  Combines data from the message store (envelope metadata), the queue release table (delivery status and response), and the sender delivery table (MX routing details).  When `groupby=recipient` each row represents one delivery attempt; when `groupby=message` delivery fields reflect one arbitrary recipient.",
        "required": [
          "_id",
          "id",
          "from",
          "to",
          "created",
          "time",
          "user",
          "transtype",
          "origin",
          "interface"
        ],
        "type": "object",
        "properties": {
          "_id": {
            "description": "Internal auto-increment database row ID.",
            "type": "integer",
            "example": 103172,
            "x-codegen-property-name": "internalId"
          },
          "id": {
            "description": "The relay-assigned mail ID (18-19 hex characters).  Matches the `mailid` filter parameter and the `text` value returned by send endpoints.",
            "type": "string",
            "example": "17c7eda538e0005d03"
          },
          "from": {
            "description": "SMTP envelope `MAIL FROM` address.",
            "type": "string",
            "example": "person@mysite.com"
          },
          "to": {
            "description": "SMTP envelope `RCPT TO` address.",
            "type": "string",
            "example": "client@isp.com"
          },
          "subject": {
            "nullable": true,
            "description": "The `Subject` header value.  MIME-encoded subjects (UTF-8, ISO-8859, US-ASCII) are automatically decoded.",
            "type": "string",
            "example": "sell 0.005 shares"
          },
          "messageId": {
            "nullable": true,
            "description": "The `Message-ID` header value.  Can be used with the `messageId` filter for subsequent lookups.",
            "type": "string",
            "example": "<vmiLEebsuCbSpUxD7oN3REpaN4VbN6BrdCAbNKIrdAo@relay0.mailbaby.net>"
          },
          "created": {
            "description": "Human-readable creation timestamp in `YYYY-MM-DD HH:MM:SS` format.",
            "type": "string",
            "example": "2021-10-14 08:50:10"
          },
          "time": {
            "description": "Unix timestamp of message acceptance.  Corresponds to the `startDate` and `endDate` filter parameters.",
            "type": "integer",
            "example": 1634215809
          },
          "user": {
            "description": "The SMTP AUTH username used to submit the message (e.g. `mb5658`).",
            "type": "string",
            "example": "mb5658"
          },
          "transtype": {
            "description": "SMTP transaction type negotiated with the relay.",
            "type": "string",
            "example": "ESMTPSA"
          },
          "origin": {
            "description": "IP address of the client that submitted the message to the relay.",
            "type": "string",
            "example": "199.231.189.154"
          },
          "interface": {
            "description": "Relay interface name that accepted the message.",
            "type": "string",
            "example": "feeder"
          },
          "sendingZone": {
            "nullable": true,
            "description": "The sending zone assigned by the relay for outbound delivery.",
            "type": "string",
            "example": "interserver"
          },
          "bodySize": {
            "nullable": true,
            "description": "Size of the message body in bytes.",
            "type": "integer",
            "example": 63
          },
          "seq": {
            "nullable": true,
            "description": "Sequence index of this recipient in a multi-recipient message. Starts at 1.",
            "type": "integer",
            "example": 1
          },
          "delivered": {
            "nullable": true,
            "description": "Delivery status flag.  `1` = successfully delivered to destination MX. `0` = queued, deferred, or failed.  `null` = delivery not yet attempted.",
            "type": "integer",
            "example": 1
          },
          "code": {
            "nullable": true,
            "description": "The SMTP response code from the destination MX server (e.g. `250`).",
            "type": "integer",
            "example": 250
          },
          "recipient": {
            "nullable": true,
            "description": "The specific recipient address this delivery record is for.",
            "type": "string",
            "example": "client@isp.com"
          },
          "response": {
            "nullable": true,
            "description": "The full SMTP response string received from the destination MX server.",
            "type": "string",
            "example": "250 2.0.0 Ok queued as C91D83E128C"
          },
          "domain": {
            "nullable": true,
            "description": "The destination domain for this delivery attempt.",
            "type": "string",
            "example": "interserver.net"
          },
          "locked": {
            "nullable": true,
            "description": "Whether the queue entry is currently locked for delivery processing.",
            "type": "integer",
            "example": 1
          },
          "lockTime": {
            "nullable": true,
            "description": "Millisecond-precision timestamp of the last queue lock acquisition.",
            "type": "string",
            "example": "1634215818533"
          },
          "assigned": {
            "nullable": true,
            "description": "The relay server node assigned to deliver this message.",
            "type": "string",
            "example": "relay1"
          },
          "queued": {
            "nullable": true,
            "description": "ISO 8601 timestamp when the message was placed into the delivery queue.",
            "type": "string",
            "example": "2021-10-14T12:50:15.487Z"
          },
          "mxHostname": {
            "nullable": true,
            "description": "The MX hostname the relay connected to for delivery.  Corresponds to the `mx` filter parameter.",
            "type": "string",
            "example": "mx.j.is.cc"
          }
        },
        "example": {
          "_id": 103172,
          "id": "17c7eda538e0005d03",
          "from": "person@mysite.com",
          "to": "client@isp.com",
          "subject": "sell 0.005 shares",
          "messageId": "<vmiLEebsuCbSpUxD7oN3REpaN4VbN6BrdCAbNKIrdAo@relay0.mailbaby.net>",
          "created": "2021-10-14 08:50:10",
          "time": 1634215809,
          "user": "mb5658",
          "transtype": "ESMTPSA",
          "origin": "199.231.189.154",
          "interface": "feeder",
          "sendingZone": "interserver",
          "bodySize": 63,
          "seq": 1,
          "delivered": 1,
          "code": 250,
          "recipient": "client@isp.com",
          "domain": "interserver.net",
          "locked": 1,
          "lockTime": "1634215818533",
          "assigned": "relay1",
          "queued": "2021-10-14T12:50:15.487Z",
          "mxHostname": "mx.j.is.cc",
          "response": "250 2.0.0 Ok queued as C91D83E128C"
        }
      },
      "SendMail": {
        "title": "SendMail",
        "description": "Details for an Email",
        "required": [
          "to",
          "from",
          "subject",
          "body"
        ],
        "type": "object",
        "properties": {
          "to": {
            "description": "The Contact whom is the primary recipient of this email.",
            "type": "string",
            "example": "johndoe@company.com"
          },
          "from": {
            "description": "The contact whom is the this email is from.",
            "type": "string",
            "example": "janedoe@company.com"
          },
          "subject": {
            "description": "The subject or title of the email",
            "type": "string",
            "example": "Attention Client"
          },
          "body": {
            "description": "The main email contents.",
            "type": "string",
            "example": "This is an email to inform you that something noteworthy happened."
          }
        }
      },
      "SendMailAdv": {
        "title": "SendMailAdv",
        "description": "Details for an Email",
        "required": [
          "from",
          "to",
          "subject",
          "body"
        ],
        "type": "object",
        "properties": {
          "subject": {
            "description": "The subject or title of the email",
            "type": "string",
            "example": "Your Package has been Delivered!"
          },
          "body": {
            "description": "The main email contents.",
            "type": "string",
            "example": "The package you ordered on 2021-01-23 has been delivered. If the package is broken into many pieces, please blaim someone else."
          },
          "from": {
            "$ref": "#/components/schemas/EmailAddressName"
          },
          "to": {
            "description": "A list of destionation email addresses to send this to",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailAddressName"
            },
            "example": [
              {
                "email": "user@domain.com",
                "name": "John Smith"
              }
            ]
          },
          "replyto": {
            "description": "(optional) A list of email addresses that specify where replies to the email should be sent instead of the _from_ address.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailAddressName"
            },
            "example": [
              {
                "email": "user@domain.com",
                "name": "John Smith"
              }
            ]
          },
          "cc": {
            "description": "(optional) A list of email addresses to carbon copy this message to.  They are listed on the email and anyone getting the email can see this full list of Contacts who received the email as well.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailAddressName"
            },
            "example": [
              {
                "email": "user@domain.com",
                "name": "John Smith"
              }
            ]
          },
          "bcc": {
            "description": "(optional) list of email addresses that should receive copies of the email.  They are hidden on the email and anyone gettitng the email would not see the other people getting the email in this list.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailAddressName"
            },
            "example": [
              {
                "email": "user@domain.com",
                "name": "John Smith"
              }
            ]
          },
          "attachments": {
            "description": "(optional) File attachments to include in the email.  The file contents must be base64 encoded!",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailAttachment"
            },
            "example": [
              {
                "filename": "text.txt",
                "data": "base64_encoded_contents"
              }
            ]
          },
          "id": {
            "format": "int64",
            "description": "(optional)  ID of the Mail order within our system to use as the Mail Account.",
            "type": "integer",
            "example": 5000
          }
        },
        "example": {
          "subject": "Welcome",
          "body": "Hello",
          "from": {
            "email": "user@domain.com"
          },
          "to": [
            {
              "email": "someone@client.com",
              "name": "Mr Client"
            }
          ],
          "attachments": [
            {
              "filename": "file.txt",
              "data": "base64_encoded_contents"
            }
          ],
          "id": 66
        }
      },
      "AffiliateDockSetup": {
        "title": "Root Type for AffiliateDockSetup",
        "description": "Affiliate Landing Page information.",
        "type": "object",
        "properties": {
          "affiliate_dock_title": {
            "type": "string"
          },
          "affiliate_dock_description": {
            "type": "string"
          },
          "referrer_coupon": {
            "type": "string"
          }
        },
        "example": {
          "affiliate_dock_title": "Exclusive offer to viewers",
          "affiliate_dock_description": "Use this coupon when placing an order to get the first month of hosting for only 1 penny.",
          "referrer_coupon": "exclusiveoffer"
        }
      },
      "AffiliatePaymentSetup": {
        "title": "Root Type for AffiliatePaymentSetup",
        "description": "Affiliate Payment Setup.  Here you can set if you want the payments to go to `prepay` or `paypal` and the PayPal email address.",
        "type": "object",
        "properties": {
          "affiliate_paypal": {
            "type": "string"
          },
          "affiliate_payment_method": {
            "type": "string"
          }
        },
        "example": {
          "affiliate_paypal": "paypal@email.com",
          "affiliate_payment_method": "prepay"
        }
      },
      "ServerIpmiPowerRequest": {
        "title": "Root Type for ServerIpmiPowerRequest",
        "description": "IPMI Power command for servers",
        "required": [
          "action"
        ],
        "type": "object",
        "properties": {
          "asset": {
            "format": "int32",
            "description": "The Asset ID",
            "type": "integer",
            "example": 5432
          },
          "action": {
            "description": "The power action to send to the ipmi controller.",
            "enum": [
              "cycle",
              "reset",
              "on",
              "off",
              "soft"
            ],
            "type": "string",
            "example": "on"
          }
        },
        "example": {
          "asset": 5432,
          "action": "reset"
        }
      },
      "ServerBulkIpmiPowerResponse": {
        "title": "Root Type for ServerBulkIpmiPowerResponse",
        "description": "Per-server IPMI power-status results for a bulk lookup.",
        "required": [
          "results"
        ],
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "description": "Server ID this result corresponds to.",
                  "type": "integer",
                  "example": 2313
                },
                "asset": {
                  "description": "Asset ID that was queried for the server (omitted on errors before asset selection).",
                  "type": "integer",
                  "example": 5432
                },
                "text": {
                  "description": "IPMI power-status output for this server.",
                  "type": "string",
                  "example": "Chassis Power is on"
                },
                "error": {
                  "description": "Error message for this server, if processing failed (mutually exclusive with `text`).",
                  "type": "string",
                  "example": "Service is not active"
                }
              }
            }
          }
        },
        "example": {
          "results": [
            {
              "id": 2313,
              "asset": 5432,
              "text": "Chassis Power is on"
            },
            {
              "id": 2314,
              "error": "Service is not active"
            },
            {
              "id": 2315,
              "error": "Invalid Service Passed"
            }
          ]
        }
      },
      "ServerIpmiLiveRequest": {
        "title": "Root Type for ServerIpmiLiveRequest",
        "description": "Request body to setup an IPMI Live connection.",
        "required": [
          "ip"
        ],
        "type": "object",
        "properties": {
          "asset": {
            "format": "int32",
            "description": "Asset ID",
            "type": "integer",
            "example": 5431
          },
          "ip": {
            "description": "Your IP Address you wish to connect to the IPMI system from.",
            "type": "string",
            "example": "1.2.3.4"
          }
        },
        "example": {
          "asset": 5431,
          "ip": "1.2.3.4"
        }
      },
      "BackupOrderPutRequest": {
        "title": "Root Type for BackupOrderPutRequest",
        "description": "Parameters to submit to validate your backup order",
        "type": "object",
        "properties": {
          "validateOnly": {
            "type": "boolean"
          },
          "serviceType": {
            "format": "int32",
            "type": "integer"
          },
          "coupon": {
            "type": "string"
          }
        },
        "example": {
          "validateOnly": true,
          "serviceType": 10831,
          "coupon": ""
        }
      },
      "BackupOrderPutResponse": {
        "title": "Root Type for BackupOrderPutResponse",
        "description": "Backup Order validation response",
        "type": "object",
        "properties": {
          "continue": {
            "type": "boolean"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "serviceType": {
            "format": "int32",
            "type": "integer"
          },
          "serviceCost": {
            "type": "string"
          },
          "originalCost": {
            "type": "string"
          },
          "repeatServiceCost": {
            "type": "string"
          },
          "hostname": {
            "type": "string"
          },
          "password": {
            "type": "string"
          },
          "coupon": {
            "type": "string"
          },
          "couponCode": {
            "format": "int32",
            "type": "integer"
          }
        },
        "example": {
          "continue": true,
          "errors": [],
          "serviceType": 10831,
          "serviceCost": "3.00",
          "originalCost": "3.00",
          "repeatServiceCost": "3.00",
          "hostname": "",
          "password": "m13dxi6K",
          "coupon": "",
          "couponCode": 0
        }
      },
      "BackupOrderPostResponse": {
        "title": "Root Type for BackupOrderPostResponse",
        "description": "Backup Order Placement Response",
        "type": "object",
        "properties": {
          "continue": {
            "type": "boolean"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "total_cost": {
            "type": "string"
          },
          "iid": {
            "type": "string"
          },
          "iids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "real_iids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "serviceId": {
            "format": "int32",
            "type": "integer"
          },
          "invoice_description": {
            "type": "string"
          },
          "cj_params": {
            "type": "object",
            "properties": {
              "containerTagId": {
                "type": "string"
              },
              "CID": {
                "type": "string"
              },
              "OID": {
                "type": "string"
              },
              "TYPE": {
                "type": "string"
              },
              "ITEM1": {
                "type": "string"
              },
              "AMT1": {
                "type": "string"
              },
              "QTY1": {
                "format": "int32",
                "type": "integer"
              },
              "CURRENCY": {
                "type": "string"
              }
            }
          }
        },
        "example": {
          "continue": true,
          "errors": [],
          "total_cost": "3.00",
          "iid": "25296597",
          "iids": [
            "SERVICEbackups40464"
          ],
          "real_iids": [
            "25296597"
          ],
          "serviceId": 40464,
          "invoice_description": "Storage ST 100",
          "cj_params": {
            "containerTagId": "1684",
            "CID": "1525038",
            "OID": "backups40464",
            "TYPE": "355669",
            "ITEM1": "backups10831",
            "AMT1": "3.00",
            "QTY1": 1,
            "CURRENCY": "USD"
          }
        }
      },
      "ServiceOrderPostResponse": {
        "title": "ServiceOrderPostResponse",
        "description": "Generic response returned after placing a service order. Contains invoice IDs for payment and the new service ID.",
        "type": "object",
        "properties": {
          "continue": {
            "description": "Whether the order was accepted and can proceed to payment.",
            "type": "boolean"
          },
          "errors": {
            "description": "List of validation errors (empty on success).",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "total_cost": {
            "description": "Total cost of the order.",
            "type": "string"
          },
          "iid": {
            "description": "Primary invoice ID for payment.",
            "type": "string"
          },
          "iids": {
            "description": "All invoice identifiers associated with the order.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "real_iids": {
            "description": "Numeric invoice IDs for use with billing endpoints.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "serviceId": {
            "format": "int32",
            "description": "The new service ID created by the order.",
            "type": "integer"
          },
          "invoice_description": {
            "description": "Human-readable description of the invoice.",
            "type": "string"
          }
        },
        "example": {
          "continue": true,
          "errors": [],
          "total_cost": "5.00",
          "iid": "25296600",
          "iids": [
            "SERVICE12345"
          ],
          "real_iids": [
            "25296600"
          ],
          "serviceId": 12345,
          "invoice_description": "New Service Order"
        }
      },
      "ServerIpmiLiveInfo": {
        "title": "Root Type for ServerIpmiLiveInfo",
        "description": "Information about the IPMI connection.",
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          },
          "public_ip": {
            "type": "string"
          },
          "allowed_ip": {
            "type": "string"
          },
          "client_username": {
            "type": "string"
          },
          "client_password": {
            "type": "string"
          }
        },
        "example": {
          "text": "Setup Complete",
          "public_ip": "1.2.3.4",
          "allowed_ip": "1.2.3.4",
          "client_username": "user",
          "client_password": "pass"
        }
      },
      "MailStatsType": {
        "title": "Root Type for MailStatsType",
        "description": "Statistics about the mail usage including volume by IP, To address, and From address; as well as total sent / delivered counts and cost.",
        "type": "object",
        "properties": {
          "time": {
            "default": "1h",
            "enum": [
              "all",
              "billing",
              "month",
              "7d",
              "24h",
              "today",
              "1h"
            ],
            "type": "string"
          },
          "usage": {
            "format": "int32",
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "currencySymbol": {
            "type": "string"
          },
          "cost": {
            "format": "double",
            "type": "number"
          },
          "received": {
            "format": "int32",
            "type": "integer"
          },
          "sent": {
            "format": "int32",
            "type": "integer"
          },
          "volume": {
            "type": "object",
            "properties": {
              "to": {
                "type": "object",
                "properties": {
                  "client@domain.com": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "user@site.net": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "sales@company.com": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "client@anothersite.com": {
                    "format": "int32",
                    "type": "integer"
                  }
                }
              },
              "from": {
                "type": "object",
                "properties": {
                  "billing@somedomain.com": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "sales@somedomain.com": {
                    "format": "int32",
                    "type": "integer"
                  }
                }
              },
              "ip": {
                "type": "object",
                "properties": {
                  "1.1.1.1": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "2.2.2.2": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "3.3.3.3": {
                    "format": "int32",
                    "type": "integer"
                  },
                  "4.4.4.4": {
                    "format": "int32",
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "example": {
          "time": "all",
          "usage": 55,
          "currency": "USD",
          "currencySymbol": "$",
          "cost": 1.02,
          "received": 508,
          "sent": 495,
          "volume": {
            "to": {
              "client@domain.com": 395,
              "user@site.net": 57,
              "sales@company.com": 47,
              "client@anothersite.com": 9
            },
            "from": {
              "billing@somedomain.com": 369,
              "sales@somedomain.com": 139
            },
            "ip": {
              "1.1.1.1": 142,
              "2.2.2.2": 132,
              "3.3.3.3": 129,
              "4.4.4.4": 105
            }
          }
        }
      },
      "VpsOrderPutRequest": {
        "description": "request to validate a vps order",
        "required": [
          "osDistro",
          "slices",
          "vpsPlatform",
          "period",
          "location",
          "osVersion",
          "hostname",
          "rootpass"
        ],
        "type": "object",
        "properties": {
          "osDistro": {
            "description": "OS Distribution",
            "type": "string"
          },
          "slices": {
            "format": "int32",
            "description": "Number of slices",
            "default": 1,
            "maximum": 32,
            "minimum": 1,
            "type": "integer"
          },
          "vpsPlatform": {
            "description": "VPS Platform",
            "enum": [
              "kvm",
              "hyperv",
              "kvmstorage"
            ],
            "type": "string"
          },
          "controlpanel": {
            "description": "Control Panel",
            "enum": [
              "none",
              "cpanel",
              "da"
            ],
            "type": "string"
          },
          "period": {
            "format": "int32",
            "description": "Billing Period or Frequency",
            "default": 1,
            "maximum": 12,
            "minimum": 1,
            "type": "integer"
          },
          "location": {
            "format": "int32",
            "description": "Location",
            "default": 1,
            "maximum": 3,
            "minimum": 1,
            "type": "integer"
          },
          "osVersion": {
            "description": "OS Version",
            "type": "string"
          },
          "hostname": {
            "description": "The hostname to assign to the VPS",
            "default": "",
            "type": "string"
          },
          "coupon": {
            "description": "Coupon",
            "default": "",
            "type": "string"
          },
          "rootpass": {
            "description": "Root password to assign to the VVPS",
            "type": "string"
          },
          "comment": {
            "description": "Order comments or notes",
            "default": "",
            "type": "string"
          }
        },
        "example": {
          "osDistro": "ubuntu",
          "slices": 1,
          "vpsPlatform": "kvm",
          "controlpanel": "none",
          "period": 1,
          "location": 1,
          "osVersion": "ubuntu24",
          "hostname": "",
          "coupon": "",
          "rootpass": "string",
          "comment": ""
        }
      },
      "VpsOrderPostRequest": {
        "description": "request to validate a vps order",
        "required": [
          "osDistro",
          "slices",
          "vpsPlatform",
          "period",
          "location",
          "osVersion",
          "hostname",
          "rootpass"
        ],
        "type": "object",
        "properties": {
          "osDistro": {
            "description": "OS Distribution",
            "type": "string"
          },
          "slices": {
            "format": "int32",
            "description": "Number of slices",
            "default": 1,
            "maximum": 32,
            "minimum": 1,
            "type": "integer"
          },
          "vpsPlatform": {
            "description": "VPS Platform",
            "enum": [
              "kvm",
              "hyperv",
              "kvmstorage"
            ],
            "type": "string"
          },
          "controlpanel": {
            "description": "Control Panel",
            "enum": [
              "none",
              "cpanel",
              "da"
            ],
            "type": "string"
          },
          "period": {
            "format": "int32",
            "description": "Billing Period or Frequency",
            "default": 1,
            "maximum": 12,
            "minimum": 1,
            "type": "integer"
          },
          "location": {
            "format": "int32",
            "description": "Location",
            "default": 1,
            "maximum": 3,
            "minimum": 1,
            "type": "integer"
          },
          "osVersion": {
            "description": "OS Version",
            "type": "string"
          },
          "hostname": {
            "description": "The hostname to assign to the VPS",
            "default": "",
            "type": "string"
          },
          "coupon": {
            "description": "Coupon",
            "default": "",
            "type": "string"
          },
          "rootpass": {
            "description": "Root password to assign to the VVPS",
            "type": "string"
          },
          "comment": {
            "description": "Order comments or notes",
            "default": "",
            "type": "string"
          }
        },
        "example": {
          "osDistro": "ubuntu",
          "slices": 1,
          "vpsPlatform": "kvm",
          "controlpanel": "none",
          "period": 1,
          "location": 1,
          "osVersion": "ubuntu24",
          "hostname": "",
          "coupon": "",
          "rootpass": "string",
          "comment": ""
        }
      },
      "VpsOrderPutResponse": {
        "title": "Root Type for VpsOrderPutResponse",
        "description": "Response from VPS order validation request",
        "type": "object",
        "properties": {
          "continue": {
            "type": "boolean"
          },
          "errors": {
            "type": "array",
            "items": {}
          },
          "coupon_code": {
            "format": "int32",
            "type": "integer"
          },
          "service_cost": {
            "format": "int32",
            "type": "integer"
          },
          "slice_cost": {
            "format": "int32",
            "type": "integer"
          },
          "service_type": {
            "format": "int32",
            "type": "integer"
          },
          "repeat_slice_cost": {
            "format": "int32",
            "type": "integer"
          },
          "original_slice_cost": {
            "format": "int32",
            "type": "integer"
          },
          "original_cost": {
            "format": "int32",
            "type": "integer"
          },
          "repeat_service_cost": {
            "format": "int32",
            "type": "integer"
          },
          "monthly_service_cost": {
            "format": "int32",
            "type": "integer"
          },
          "custid": {
            "type": "string"
          },
          "os": {
            "type": "string"
          },
          "slices": {
            "type": "string"
          },
          "platform": {
            "type": "string"
          },
          "controlpanel": {
            "type": "string"
          },
          "period": {
            "format": "int32",
            "type": "integer"
          },
          "location": {
            "format": "int32",
            "type": "integer"
          },
          "version": {
            "type": "string"
          },
          "hostname": {
            "type": "string"
          },
          "coupon": {
            "type": "string"
          },
          "rootpass": {
            "type": "string"
          }
        },
        "example": {
          "continue": true,
          "errors": [],
          "coupon_code": 0,
          "service_cost": 3,
          "slice_cost": 3,
          "service_type": 33,
          "repeat_slice_cost": 3,
          "original_slice_cost": 3,
          "original_cost": 3,
          "repeat_service_cost": 3,
          "monthly_service_cost": 3,
          "custid": "123456",
          "os": "ubuntu",
          "slices": "1",
          "platform": "kvm",
          "controlpanel": "none",
          "period": 1,
          "location": 1,
          "version": "ubuntu24",
          "hostname": "server.blank.com",
          "coupon": "",
          "rootpass": "string"
        }
      },
      "DnsNewRecord": {
        "title": "Root Type for DnsNewRecord",
        "description": "Request data for a new DNS record.",
        "required": [
          "type",
          "content",
          "name"
        ],
        "type": "object",
        "properties": {
          "name": {
            "description": "Name part of record",
            "type": "string",
            "example": "myfamily.com"
          },
          "type": {
            "$ref": "#/components/schemas/DnsRecordType"
          },
          "content": {
            "description": "Content of record",
            "type": "string",
            "example": "127.0.0.1"
          },
          "ttl": {
            "format": "int32",
            "description": "Time-to-live",
            "default": 86400,
            "type": "integer",
            "example": 86400
          },
          "prio": {
            "format": "int32",
            "description": "Priority",
            "default": 0,
            "type": "integer",
            "example": 0
          }
        },
        "example": {
          "name": "myfamily.com",
          "type": "NS",
          "content": "cdns1.interserver.net",
          "ttl": 86400,
          "prio": 0
        }
      },
      "DnsRecordType": {
        "description": "Type of DNS Record",
        "enum": [
          "A",
          "A6",
          "AAAA",
          "AFSDB",
          "ALIAS",
          "CAA",
          "CDNSKEY",
          "CDS",
          "CERT",
          "CNAME",
          "DHCID",
          "DLV",
          "DNSKEY",
          "DNAME",
          "DS",
          "EUI48",
          "EUI64",
          "HINFO",
          "IPSECKEY",
          "KEY",
          "KX",
          "LOC",
          "MAILA",
          "MAILB",
          "MINFO",
          "MR",
          "MX",
          "NAPTR",
          "NS",
          "NSEC",
          "NSEC3",
          "NSEC3PARAM",
          "OPENPGPKEY",
          "OPT",
          "PTR",
          "RKEY",
          "RP",
          "RRSIG",
          "SIG",
          "SOA",
          "SPF",
          "SRV",
          "SSHFP",
          "TLSA",
          "TKEY",
          "TSIG",
          "TXT",
          "WKS",
          "URI⏎"
        ],
        "type": "string",
        "example": "NS"
      },
      "TicketNew": {
        "title": "Root Type for TicketNew",
        "description": "New helpdesk ticket request.\r\n",
        "required": [
          "subject",
          "body"
        ],
        "type": "object",
        "properties": {
          "subject": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "service_id": {
            "format": "int32",
            "type": "integer"
          },
          "service_module": {
            "type": "string"
          }
        },
        "example": {
          "subject": "subject of ticket",
          "body": "ticket contents",
          "service_id": 1231,
          "service_module": "vps"
        }
      },
      "TicketNewResponse": {
        "title": "Root Type for TicketNewSuccess",
        "description": "Response returned after creating a new support ticket.",
        "required": [
          "text",
          "success"
        ],
        "type": "object",
        "properties": {
          "ticket_id": {
            "type": "integer"
          },
          "text": {
            "type": "string"
          },
          "success": {
            "type": "boolean"
          }
        },
        "example": {
          "success": true,
          "text": "Ticket is created!",
          "ticket_id": 1759653
        }
      },
      "ViewTicketResponse": {
        "title": "Root type for ViewTicketResponse",
        "description": "Ticket details",
        "required": [
          "success",
          "ticket"
        ],
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "ticket": {
            "$ref": "#/components/schemas/TicketDetails"
          },
          "ticket_custom_fields": {
            "$ref": "#/components/schemas/TicketCustomFieldDetails"
          },
          "ticket_posts": {
            "$ref": "#/components/schemas/TicketPostDetails"
          }
        },
        "example": {
          "success": true,
          "ticket": {
            "ticketid": 123456,
            "ticketmaskid": "UAX-553-25735",
            "department": "General",
            "status": "Closed",
            "priority": "Standard",
            "subject": "Regarding ssl doubts",
            "created_on": "19 November, 2025 07:24 pm",
            "updated_on": "20 November, 2025 05:41 pm"
          },
          "ticket_custom_fields": {
            "Customer Server Access": "y",
            "Ip Address": "68.23.3.5",
            "Root Password": "fsdnbfsd",
            "Sudo User": "jjog",
            "Sudo Password": 123,
            "Port": 22
          },
          "ticket_posts": [
            {
              "post_id": 24244,
              "date": "20 November, 2025 05:41 pm",
              "contents": "Testing ticket reply from api",
              "creator": "User",
              "creator_email": "abc@email.com",
              "creator_name": "ABC DEF",
              "hasattachments": 0
            },
            {
              "post_id": 24244,
              "date": "21 November, 2025 05:41 pm",
              "contents": "Test 234",
              "creator": "User",
              "creator_email": "john@email.com",
              "creator_name": "John Wick",
              "hasattachments": 1,
              "attachment_download": "https:my.interserver.net/download_link?id=xxx"
            }
          ]
        }
      },
      "TicketDetails": {
        "title": "Ticket Information",
        "description": "Detailed ticket information",
        "type": "object",
        "properties": {
          "ticketid": {
            "type": "integer"
          },
          "ticketmaskid": {
            "type": "string"
          },
          "department": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "priority": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "created_on": {
            "type": "string"
          },
          "updated_on": {
            "type": "string"
          }
        }
      },
      "TicketCustomFieldDetails": {
        "title": "Ticket custom field values",
        "description": "Optional fields providing additional info in ticket",
        "type": "object",
        "properties": {
          "Customer Server Access": {
            "enum": [
              "y",
              "n"
            ],
            "type": "string"
          },
          "Ip Address": {
            "type": "string"
          },
          "Root Password": {
            "type": "string"
          },
          "Sudo User": {
            "type": "string"
          },
          "Sudo Password": {
            "type": "integer"
          },
          "Port": {
            "type": "integer"
          }
        }
      },
      "TicketPostDetails": {
        "title": "Ticket Posts",
        "description": "Fetches every posts of ticket",
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "post_id": {
              "type": "integer"
            },
            "date": {
              "type": "string"
            },
            "contents": {
              "type": "string"
            },
            "creator": {
              "enum": [
                "User",
                "Staff"
              ],
              "type": "string"
            },
            "creator_email": {
              "type": "string"
            },
            "creator_name": {
              "type": "string"
            },
            "hasattachments": {
              "type": "integer"
            },
            "attachment_download": {
              "type": "string"
            }
          }
        }
      },
      "UpdateTicket": {
        "title": "Update Ticket",
        "description": "Update custom fields of the ticket",
        "type": "object",
        "properties": {
          "ip": {
            "type": "string"
          },
          "ip_address": {
            "type": "string"
          },
          "customer_server_access": {
            "enum": [
              "y",
              "n"
            ],
            "type": "string"
          },
          "root_password": {
            "type": "string"
          },
          "sudo_username": {
            "type": "string"
          },
          "sudo_password": {
            "type": "string"
          },
          "port": {
            "type": "integer"
          }
        },
        "example": {
          "ip": "1.2.4.5",
          "customer_server_access": "y",
          "sudo_username": "mywebsp",
          "sudo_password": "abc123",
          "port": 22
        }
      },
      "UpdateTicketResponseSchema": {
        "title": "Update ticket response",
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "message": {
            "type": "string"
          }
        },
        "example": {
          "success": true,
          "message": "Ticket is updated!"
        }
      },
      "ReplyTicketRequest": {
        "title": "Post reply",
        "description": "Post reply to your ticket",
        "type": "object",
        "properties": {
          "content": {
            "type": "string"
          }
        },
        "example": {
          "content": "Requesting to migrate website 123"
        }
      },
      "ReplyTicketResponseSchema": {
        "title": "Post Reply to your ticket",
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "post_id": {
            "type": "integer"
          }
        },
        "example": {
          "success": true,
          "post_id": 234452
        }
      },
      "CloseTicketResponseSchema": {
        "title": "Close ticket",
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "text": {
            "type": "string"
          }
        },
        "example": {
          "success": true,
          "text": "Ticket is closed!"
        }
      },
      "CreateFirewallRule": {
        "title": "Create Firewall Rule",
        "description": "Create firewall rule for your ip",
        "required": [
          "protocol_id",
          "xdp_action"
        ],
        "type": "object",
        "properties": {
          "destination_port": {
            "default": 80,
            "type": "integer",
            "example": 22
          },
          "source_ip": {
            "description": "Source IP address to match. Use '0.0.0.0' to match any source.",
            "default": "0.0.0.0",
            "type": "string",
            "example": "1.2.4.5"
          },
          "source_port": {
            "default": 0,
            "type": "integer",
            "example": 1302
          },
          "protocol_id": {
            "description": "1 = TCP, 2 = UDP",
            "enum": [
              1,
              2
            ],
            "type": "integer",
            "example": 1
          },
          "xdp_action": {
            "description": "1 = Block,  0 = Whitelist",
            "enum": [
              0,
              1
            ],
            "type": "integer",
            "example": 1
          }
        }
      },
      "CreateGeoFirewallRule": {
        "title": "Create Firewall Rule",
        "description": "Create firewall rule for your ip",
        "required": [
          "xdp_action"
        ],
        "type": "object",
        "properties": {
          "destination_port": {
            "default": 80,
            "type": "integer",
            "example": 22
          },
          "country_code": {
            "description": "To get country code refer our countries api - https://my.interserver.net/apiv2/account/countries?fetch_by=numcode",
            "type": "integer",
            "example": 10
          },
          "asn": {
            "description": "ASN number",
            "type": "integer",
            "example": 1331
          },
          "xdp_action": {
            "description": "1 = Block,  0 = Whitelist",
            "enum": [
              0,
              1
            ],
            "type": "integer",
            "example": 1
          }
        }
      },
      "CreateFilter": {
        "title": "Create Filter",
        "description": "Create firewall rule for your ip",
        "required": [
          "filter_type",
          "port"
        ],
        "type": "object",
        "properties": {
          "filter_type": {
            "type": "string",
            "example": "cs2_udp"
          },
          "port": {
            "type": "integer",
            "example": 8080
          }
        }
      },
      "ScrubIpPlaceOrder": {
        "title": "Place ScrubIP Order",
        "description": "Place ScrubIP Order",
        "required": [
          "service_type",
          "ip"
        ],
        "type": "object",
        "properties": {
          "service_type": {
            "type": "integer",
            "example": 102
          },
          "ip": {
            "type": "string",
            "example": "1.2.3.4"
          }
        }
      },
      "BuyItNowList": {
        "title": "Marketpalce servers list",
        "description": "Marketplace buy it now servers list",
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/BuyItNowRow"
        }
      },
      "BuyItNowRow": {
        "description": "A buy-it-now dedicated server listing from the marketplace, with hardware specs and pricing.",
        "type": "object",
        "properties": {
          "server_id": {
            "description": "Unique server ID. Use this as `server_id` in `POST /servers/order/buy_now_server`.",
            "type": "string",
            "example": "11432"
          },
          "cpu": {
            "type": "array",
            "items": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "object",
                  "properties": {
                    "img": {
                      "type": "string",
                      "example": "xeon-e3.png"
                    },
                    "type": {
                      "type": "string",
                      "example": "XEON"
                    },
                    "speed": {
                      "type": "string",
                      "example": ""
                    },
                    "num_cpus": {
                      "type": "string",
                      "example": "1"
                    },
                    "num_cores": {
                      "type": "string",
                      "example": "4"
                    }
                  }
                }
              ]
            }
          },
          "memory": {
            "type": "string",
            "example": "64GB"
          },
          "disk": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "bandwidth": {
            "type": "string",
            "example": "1Gbps Unmetered"
          },
          "ips": {
            "type": "string",
            "example": "1 Vlan Ip (/30)"
          },
          "location": {
            "type": "string",
            "example": "NYC Region"
          },
          "price": {
            "type": "integer",
            "example": 64
          }
        }
      },
      "StatusMonthlyBreakdown": {
        "description": "Monthly mail delivery status breakdown, showing counts per status category.",
        "required": [
          "default",
          "failed",
          "rejected",
          "pending",
          "locked",
          "paid"
        ],
        "type": "object",
        "properties": {
          "default": {
            "$ref": "#/components/schemas/MonthlyCounts"
          },
          "failed": {
            "$ref": "#/components/schemas/MonthlyCounts"
          },
          "rejected": {
            "$ref": "#/components/schemas/MonthlyCounts"
          },
          "pending": {
            "$ref": "#/components/schemas/MonthlyCounts"
          },
          "locked": {
            "$ref": "#/components/schemas/MonthlyCounts"
          },
          "paid": {
            "$ref": "#/components/schemas/MonthlyCounts"
          }
        }
      },
      "MonthlyCounts": {
        "description": "A map of month labels (e.g., \"2024-01\") to counts for a specific mail status.",
        "type": "object",
        "additionalProperties": {
          "type": "integer"
        }
      },
      "DomainNameserverGetResponse": {
        "description": "List of registered nameserver hosts with glue record metadata.",
        "type": "array",
        "items": {
          "required": [
            "name",
            "ipaddress",
            "can_delete"
          ],
          "type": "object",
          "properties": {
            "name": {
              "format": "hostname",
              "type": "string",
              "example": "ns1.domain.com"
            },
            "ipaddress": {
              "format": "ip",
              "type": "string",
              "example": "1.2.3.4"
            },
            "can_delete": {
              "description": "Whether the registrar allows deletion of this nameserver entry.",
              "enum": [
                "0",
                "1"
              ],
              "type": "string",
              "example": "1"
            }
          }
        }
      },
      "DomainNameserverPostRequest": {
        "description": "Payload for adding a registered nameserver (glue record).",
        "required": [
          "name",
          "ipAddress"
        ],
        "type": "object",
        "properties": {
          "name": {
            "format": "hostname",
            "type": "string",
            "example": "ns1.host.com"
          },
          "ipAddress": {
            "format": "ip",
            "type": "string",
            "example": "1.2.3.4Get"
          }
        }
      },
      "DomainNameserverPutRequest": {
        "description": "Payload for replacing the assigned nameserver list for a domain.",
        "required": [
          "nameserver"
        ],
        "type": "object",
        "properties": {
          "nameserver": {
            "type": "array",
            "items": {
              "type": "string",
              "example": "ns1.example.com"
            }
          }
        }
      },
      "RestoreRequest": {
        "title": "Root Type for RestoreRequest",
        "description": "Request data to trigger a restore from backup.",
        "type": "object",
        "properties": {
          "backup": {
            "type": "string"
          },
          "password": {
            "type": "string"
          }
        },
        "example": {
          "backup": "vps1234-backup",
          "password": "mypaassword"
        }
      },
      "WebsiteBackups": {
        "description": "List of available backups for a webhosting service with their names and sizes.",
        "type": "array",
        "items": {
          "required": [
            "name"
          ],
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "size": {
              "type": "integer"
            }
          }
        }
      },
      "ServersBuyNowResponse": {
        "description": "Success response after placing a buy-it-now dedicated server order.",
        "type": "object",
        "properties": {
          "success": {
            "description": "Whether the order was placed successfully.",
            "type": "boolean",
            "example": true
          },
          "text": {
            "description": "Human-readable status message.",
            "type": "string",
            "example": "Server order is placed."
          },
          "order_details": {
            "description": "Details of the placed order.",
            "type": "object",
            "properties": {
              "service_id": {
                "description": "The newly created service ID for the ordered server.",
                "type": "number",
                "example": 1234
              },
              "invoice_id": {
                "description": "The invoice ID generated for the order.",
                "type": "number",
                "example": 2342355
              }
            }
          }
        }
      },
      "ServersBuyNowError": {
        "description": "Error response when a buy-it-now server order fails validation.",
        "type": "object",
        "properties": {
          "success": {
            "description": "Always false for error responses.",
            "type": "boolean",
            "example": false
          },
          "text": {
            "description": "Human-readable error summary.",
            "type": "string",
            "example": "Unable to place order."
          },
          "errors": {
            "description": "List of specific validation error messages.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "Server ID is missing.",
              "Server Hostname is missing.;",
              "Password must contain atleast 8 characters, one lowercase letter, one uppercase letter, one number & one special character"
            ]
          }
        }
      }
    },
    "responses": {
      "BadInput": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "code",
                "message"
              ],
              "type": "object",
              "properties": {
                "code": {
                  "type": "integer"
                },
                "message": {
                  "type": "string"
                }
              }
            },
            "examples": {
              "InvalidTimezone": {
                "value": {
                  "code": 422,
                  "message": "Invalid timezone"
                }
              },
              "MissingCaptcha": {
                "value": {
                  "code": 422,
                  "message": "missing captcha"
                }
              }
            }
          }
        },
        "description": "The specified resource was not found"
      },
      "LoginResponseError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/LoginErrorResponse"
            },
            "examples": {
              "MissingTwoFactorCode": {
                "value": {
                  "message": "missing 2fa",
                  "field": "tfa"
                }
              },
              "InvalidTwoFactorCode": {
                "value": {
                  "message": "invalid 2fa",
                  "field": "tfa"
                }
              },
              "MissingCaptcha": {
                "value": {
                  "message": "missing captcha",
                  "field": "captcha"
                }
              },
              "TooManyVerificationAttempts": {
                "value": {
                  "message": "max tries treached on code, sending new one, submit again",
                  "field": "verify"
                }
              }
            }
          }
        },
        "description": "Error response to a login request."
      },
      "NotFound": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "code",
                "message"
              ],
              "type": "object",
              "properties": {
                "code": {
                  "type": "integer"
                },
                "message": {
                  "type": "string"
                }
              }
            },
            "examples": {
              "InvalidVps": {
                "value": {
                  "code": 404,
                  "message": "Invalid VPS Passed"
                }
              },
              "InvalidService": {
                "value": {
                  "code": 404,
                  "message": "Invalid Service Passed"
                }
              }
            }
          }
        },
        "description": "The specified resource was not found"
      },
      "ServiceOrderPostResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ServiceOrderPostResponse"
            }
          }
        },
        "links": {
          "InitiatePayment": {
            "operationId": "getBillingInvoice",
            "parameters": {
              "id": "$response.body#/iid"
            },
            "description": "Use the invoice ID from the order response to retrieve invoice details or proceed to payment."
          }
        },
        "description": "Order placed successfully. Use the invoice ID to proceed to payment via `/billing/pay/{method}/{invoices}` or view the invoice at `/billing/invoices/{id}`."
      },
      "QueueResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/QueueResponse"
            }
          }
        },
        "description": "Response message from sending a service queue."
      },
      "SuccessTextResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/SuccessTextResponse"
            }
          }
        },
        "description": "A response indicating the operation completed successfully with a text message."
      },
      "TextResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TextResponse"
            }
          }
        },
        "description": "Response with a text message field."
      },
      "AccountValidationError": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TextResponse"
            },
            "examples": {
              "InvalidTimeZone": {
                "value": {
                  "message": "Invalid Time Zone"
                }
              },
              "MissingRequiredFields": {
                "value": {
                  "message": "fname, lname cannot be empty!"
                }
              }
            }
          }
        },
        "description": "Validation error while updating account data."
      },
      "DomainSearchNoResults": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TextResponse"
            },
            "examples": {
              "InvalidSearchResponse": {
                "value": {
                  "message": "Invalid Search Response!  Either there was an error communicating with the registrars or We received no results."
                }
              }
            }
          }
        },
        "description": "No search suggestions or registrar response available."
      },
      "InvalidDomain": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TextResponse"
            },
            "examples": {
              "InvalidDomainName": {
                "value": {
                  "message": "Invalid Domain"
                }
              }
            }
          }
        },
        "description": "The domain input was invalid."
      },
      "InvalidIpAddress": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TextResponse"
            },
            "examples": {
              "InvalidIp": {
                "value": {
                  "message": "Invalid IP Address"
                }
              }
            }
          }
        },
        "description": "IP limit payload contains an invalid address."
      },
      "InvalidTwoFactorCode": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TextResponse"
            },
            "examples": {
              "InvalidCode": {
                "value": {
                  "message": "Invalid Code"
                }
              }
            }
          }
        },
        "description": "Provided two-factor verification code was invalid."
      },
      "NothingToUpdate": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TextResponse"
            },
            "examples": {
              "NoChanges": {
                "value": {
                  "message": "Nothing to update"
                }
              }
            }
          }
        },
        "description": "Request was valid but did not contain updatable values."
      },
      "Unauthorized": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "code",
                "message"
              ],
              "type": "object",
              "properties": {
                "code": {
                  "type": "integer"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        },
        "description": "Unauthorized"
      },
      "WebsiteCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "Website is canceled."
              }
            }
          }
        },
        "description": "Website cancel"
      },
      "VPSCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "VPS is canceled."
              }
            }
          }
        },
        "description": "VPS Cancel"
      },
      "DomainsCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "Domains is canceled."
              }
            }
          }
        },
        "description": "Domains Cancel"
      },
      "BackupsCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "Backups is canceled."
              }
            }
          }
        },
        "description": "Backups Cancel"
      },
      "MailCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "Mail is canceled."
              }
            }
          }
        },
        "description": "Mail Cancel"
      },
      "LicensesCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "License is canceled."
              }
            }
          }
        },
        "description": "License Cancel"
      },
      "SSLCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "SSL is canceled."
              }
            }
          }
        },
        "description": "SSL Cancel"
      },
      "FloatingIpsCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "Floating IPs is canceled."
              }
            }
          }
        },
        "description": "Floating Ip Cancel"
      },
      "QuickserversCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "Rapid Deploy Servers is canceled."
              }
            }
          }
        },
        "description": "Rapid Deploy Servers Cancel"
      },
      "ServerCancelResponse": {
        "content": {
          "application/json": {
            "schema": {
              "required": [
                "success",
                "text"
              ],
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean"
                },
                "text": {
                  "type": "string"
                }
              },
              "example": {
                "success": true,
                "text": "Servers is canceled."
              }
            }
          }
        },
        "description": "Servers Cancel"
      },
      "LoginSuccessResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/LoginSuccessResponse"
            }
          }
        },
        "links": {
          "getAccountInfo": {
            "operationId": "getAccountInfo",
            "description": "After a successful login, use the session token to fetch account details via `GET /account`."
          }
        },
        "description": "Successful login response containing the session token."
      },
      "ServerIpmiGetResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ServerIpmiLiveInfo"
            }
          }
        },
        "description": "Response from the Servers IPMI Live information request."
      },
      "TicketNewResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/TicketNewResponse"
            }
          }
        },
        "description": "A successful response after creating a ticket"
      },
      "ViewTicketResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ViewTicketResponse"
            }
          }
        },
        "description": "Ticket Information"
      },
      "UpdateTicketResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/UpdateTicketResponseSchema"
            }
          }
        },
        "description": "Response from updating a support ticket."
      },
      "ReplyTicketResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ReplyTicketResponseSchema"
            }
          }
        },
        "description": "Post Reply to the ticket."
      },
      "CloseTicketResponse": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/CloseTicketResponseSchema"
            }
          }
        },
        "description": "Close ticket."
      },
      "scrubIpsLists": {
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ScrubIpsRowSchema"
              }
            },
            "examples": {
              "ScrubIpsListExample": {
                "value": [
                  {
                    "scrub_ip_id": 123,
                    "repeat_invoices_cost": 5,
                    "scrub_ip_ip": "1.2.3.5",
                    "scrub_ip_status": "active",
                    "services_name": "Current IP + Scrub"
                  },
                  {
                    "scrub_ip_id": 456,
                    "repeat_invoices_cost": 5,
                    "scrub_ip_ip": "4.3.2.1",
                    "scrub_ip_status": "expired",
                    "services_name": "Current IP + Scrub"
                  }
                ]
              }
            }
          }
        },
        "description": "Scrub Ips list"
      },
      "CreateFirewallResponse": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": true
                },
                "text": {
                  "type": "string",
                  "example": "New firewall Rule has been created."
                }
              }
            }
          }
        },
        "description": "Create firewall rule for scrub ip"
      },
      "DeleteFirewallResponse": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": true
                },
                "text": {
                  "type": "string",
                  "example": "Firewall Rule has been deleted."
                }
              }
            }
          }
        },
        "description": "Delete firewall rule for scrub ip"
      },
      "CreateFirewall400Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Bad Request"
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "example": "Source IP is not valid"
                  }
                }
              }
            }
          }
        },
        "description": "Bad Request"
      },
      "CreateGeoFirewall400Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Bad Request"
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "example": "Country or Asn is required."
                  }
                }
              }
            }
          }
        },
        "description": "Bad Request"
      },
      "DeleteFirewall400Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Invalid rule id"
                }
              }
            }
          }
        },
        "description": "Bad Request"
      },
      "CreateFirewall500Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Unable to create new firewall Rule."
                }
              }
            }
          }
        },
        "description": "Internal Server Error"
      },
      "DeleteFirewall500Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Unable to deleted firewall rule."
                }
              }
            }
          }
        },
        "description": "Internal Server Error"
      },
      "CreateFilterResponse": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": true
                },
                "text": {
                  "type": "string",
                  "example": "New filter has been created."
                }
              }
            }
          }
        },
        "description": "Request OK"
      },
      "DeleteFilterResponse": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": true
                },
                "text": {
                  "type": "string",
                  "example": "Filter is deleted."
                }
              }
            }
          }
        },
        "description": "Delete filter for scrub ip"
      },
      "CreateFilter400Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Bad Request"
                },
                "errors": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "example": "Filter type is invalid"
                  }
                }
              }
            }
          }
        },
        "description": "Bad Request"
      },
      "DeleteFilter400Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Port is required."
                }
              }
            }
          }
        },
        "description": "Bad Request"
      },
      "CreateFilter500Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Unable to create new filter."
                }
              }
            }
          }
        },
        "description": "Internal Server Error"
      },
      "DeleteFilter500Err": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": false
                },
                "text": {
                  "type": "string",
                  "example": "Unable to delete filter."
                }
              }
            }
          }
        },
        "description": "Internal Server Error"
      },
      "OrderScrubGetResponse": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "packageCosts": {
                  "type": "object",
                  "properties": {
                    "package_id": {
                      "type": "number",
                      "example": 11552
                    },
                    "package_cost": {
                      "type": "number",
                      "example": 5
                    },
                    "currency": {
                      "type": "string",
                      "example": "USD"
                    },
                    "currencySymbol": {
                      "type": "string",
                      "example": "$"
                    }
                  }
                },
                "serviceTypes": {
                  "type": "array",
                  "items": {
                    "properties": {
                      "services_id": {
                        "type": "integer",
                        "example": 11552
                      },
                      "services_name": {
                        "type": "string",
                        "example": "Current IP + Scrub"
                      },
                      "services_cost": {
                        "type": "integer",
                        "example": 5
                      },
                      "services_field1": {
                        "type": "string",
                        "example": ""
                      },
                      "services_field2": {
                        "type": "string",
                        "example": ""
                      },
                      "services_module": {
                        "type": "string",
                        "example": "scrub_ips"
                      }
                    }
                  }
                },
                "ips": {
                  "type": "array",
                  "items": {
                    "properties": {
                      "service_id": {
                        "type": "number",
                        "example": 12345
                      },
                      "service_module": {
                        "type": "string",
                        "example": "vps"
                      },
                      "service_hostname": {
                        "type": "string",
                        "example": "server.gtest.com"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "description": "Order details"
      },
      "ScrubIpPlaceOrder": {
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "success": {
                  "type": "boolean",
                  "example": true
                },
                "text": {
                  "type": "string",
                  "example": "ScrubIp order is placed."
                },
                "order_details": {
                  "type": "object",
                  "properties": {
                    "total_cost": {
                      "type": "integer",
                      "example": 5
                    },
                    "service_id": {
                      "type": "integer",
                      "example": 12346
                    },
                    "invoice_id": {
                      "type": "integer",
                      "example": 2746273
                    },
                    "invoice_description": {
                      "type": "string",
                      "example": "Scrub + Current Ip"
                    },
                    "cj_params": {
                      "type": "object",
                      "properties": {
                        "containerTagId": {
                          "type": "integer",
                          "example": 1684
                        },
                        "CID": {
                          "type": "integer",
                          "example": 2314
                        },
                        "OID": {
                          "type": "string",
                          "example": "scrub_ips12424"
                        },
                        "TYPE": {
                          "type": "integer",
                          "example": 2242343242
                        },
                        "ITEM1": {
                          "type": "string",
                          "example": "scrub_ips904"
                        },
                        "AMT1": {
                          "type": "integer",
                          "example": 5
                        },
                        "QTY1": {
                          "type": "integer",
                          "example": 1
                        },
                        "CURRENCY": {
                          "type": "string",
                          "example": "USD"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "description": "Request OK"
      },
      "ScrubIpLogs": {
        "content": {
          "application/json": {
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ScrubIpsLogRowSchema"
              }
            },
            "examples": {
              "ScrubIpsListExample": {
                "value": [
                  {
                    "date": "Feb 24, 2026 @ 05:39 pm",
                    "filter": "TCP FW Drop",
                    "blocked_ip": "49.36.168.21",
                    "target_ip": "173.214.163.18",
                    "target_port": 2083,
                    "protocol": "TCP",
                    "byte_count": 1514,
                    "xdp_action": "Allow"
                  },
                  {
                    "date": "Feb 24, 2026 @ 05:39 pm",
                    "filter": "Generic UDP",
                    "blocked_ip": "67.215.86.57",
                    "target_ip": "173.214.163.18",
                    "target_port": 53,
                    "protocol": "UDP",
                    "byte_count": 84,
                    "xdp_action": "Allow"
                  }
                ]
              }
            }
          }
        },
        "description": "Scrub Ips logs"
      }
    },
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "apiKey",
        "description": "In order to interact with the InterServer API you will need to first authenticate.  We support a number of different authentication methods.\n\nTo authenticate with the API Key you must get a key from your [Account Security](https://my.interserver.net/account_settings) page.\n\nTo use this key pass it in a header called `X-API-KEY`.\n\n```\ncurl -H \"X-API-KEY: $YOURE_KEY\" \"https://my.interserver.net/apiv2/$CALL\"\n````\n",
        "name": "X-API-KEY",
        "in": "header"
      },
      "sessionIdHeaderAuth": {
        "type": "apiKey",
        "description": "In order to interact with the InterServer API you will need to first authenticate.  We support a number of different authentication methods.\n\nTo authenticate with the Session ID in a HTTP header you need to first get the Session ID from a Login request.\n\nTo use this key pass it in a header called `sessionid`.\n\n```\ncurl -H \"seessionid: $YOUR_SESSION_ID\" \"https://my.interserver.net/apiv2/$CALL\"\n````\n",
        "name": "sessionid",
        "in": "header"
      },
      "sessionIdCookieAuth": {
        "type": "apiKey",
        "description": "In order to interact with the InterServer API you will need to first authenticate.  We support a number of different authentication methods.\n\nTo authenticate with the Session ID in a Cookie you need to first get the Session ID from a Login request.\n\nTo use this key pass it in a cookie called `sessionid`.\n\n```\ncurl -b \"seessionid: $YOUR_SESSION_ID\" \"https://my.interserver.net/apiv2/$CALL\"\n````\n",
        "name": "sessionid",
        "in": "cookie"
      }
    }
  }
}