{
 "openapi": "3.1.0",
 "info": {
  "title": "Gyro Public API",
  "version": "2.30.0",
  "summary": "Manage Gyro cloud servers, SSH keys and billing programmatically.",
  "x-summary-tr": "Gyro bulut sunucularınızı, SSH anahtarlarınızı ve faturalarınızı programla yönetin.",
  "contact": {
   "name": "Gyro Hosting",
   "url": "https://www.gyrohosting.com"
  },
  "license": {
   "name": "Proprietary",
   "identifier": "LicenseRef-Gyro-API-Terms"
  }
 },
 "servers": [
  {
   "url": "https://www.gyrohosting.com/api/v1/client",
   "description": "Production"
  }
 ],
 "paths": {
  "/cloud/servers": {
   "post": {
    "tags": [
     "Servers"
    ],
    "operationId": "createServer",
    "summary": "Create (order) a cloud server",
    "x-summary-tr": "Bulut sunucu oluştur (sipariş)",
    "description": "Places an order and pays it **from your account balance** (never from a stored card).\nSafety rules:\n* send `\"dry_run\": true` first to get a price quote; nothing is created;\n* a real order needs `\"confirm\": true` **and** an `Idempotency-Key` header — retrying with the\n  same key and body returns the first answer (`Idempotency-Replayed: true`) instead of a second order;\n* the operator may cap the number of servers per account (`409 server_limit`).\nProvisioning is asynchronous (typically under a minute): poll `GET /cloud/servers/{id}` until\n`status` is `active` and `live.power_state` is `running`.\n\n**Prepaid products** (`billing: prepaid`, e.g. tr-ist-1): `cycle` is ignored; the order total is 0 and usage is charged hourly from the balance (at most the monthly price per month). The balance must cover the first 24 hours, otherwise 422 with the amount to top up (GET /cloud/billing/topup-url). During the first 7 days a new account may run at most 5 prepaid servers and 1 container environment (raised by a support ticket; see GET /cloud/account/limits). Over the limit: 422 `limit_exceeded` with `details.how_to_increase`; a `dry_run` quote shows the same as `quote.limit_exceeded`.\n\n**Batch (2.21.0):** `quantity` creates N servers in one order (one invoice), each a separate service; `name` (with `{n}`) and `tags` label them. `app` installs a one-click app on each.",
    "x-description-tr": "Sipariş oluşturur ve **hesap bakiyenizden** öder (kayıtlı karttan asla çekmez).\nGüvenlik kuralları:\n* önce `\"dry_run\": true` ile fiyat teklifi alın; hiçbir şey oluşturulmaz;\n* gerçek sipariş için `\"confirm\": true` **ve** `Idempotency-Key` başlığı zorunludur — aynı anahtar ve\n  gövdeyle tekrar denemek ikinci sipariş yerine ilk yanıtı döndürür (`Idempotency-Replayed: true`);\n* yönetici hesap başına sunucu sınırı koyabilir (`409 server_limit`).\nKurulum eşzamansızdır (genellikle bir dakikadan kısa): `status` `active` ve `live.power_state`\n`running` olana kadar `GET /cloud/servers/{id}` ile sorgulayın.\n\n\n**Toplu (2.21.0):** `quantity` tek siparişte (tek fatura) N sunucu oluşturur, her biri ayrı hizmettir; `name` (`{n}` ile) ve `tags` adlandırır. `app` her birine tek tık uygulama kurar.",
    "x-scope": "cloud_order",
    "parameters": [
     {
      "$ref": "#/components/parameters/IdempotencyKey"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CreateServerRequest"
       },
       "example": {
        "product_id": 723,
        "cycle": "monthly",
        "image": "debian-12",
        "ssh_keys": [
         12
        ],
        "hostname": "web-1.example.com",
        "confirm": true
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Dry-run quote (nothing created)",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "dry_run": {
             "type": "boolean",
             "const": true
            },
            "quote": {
             "$ref": "#/components/schemas/Quote"
            }
           }
          }
         }
        }
       }
      }
     },
     "202": {
      "description": "Order placed and paid; the server is being provisioned",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "data": {
           "$ref": "#/components/schemas/CreateServerResult"
          }
         }
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/BadRequest"
     },
     "401": {
      "$ref": "#/components/responses/Unauthorized"
     },
     "402": {
      "$ref": "#/components/responses/PaymentRequired"
     },
     "403": {
      "$ref": "#/components/responses/Forbidden"
     },
     "409": {
      "$ref": "#/components/responses/Conflict"
     },
     "422": {
      "description": "confirm_required | limit_exceeded (new-account limit of the prepaid cloud; details = LimitHint, raise it with a support ticket) | validation errors",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "x-cli": {}
   }
  }
 },
 "components": {
  "schemas": {
   "Error": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "object",
      "required": [
       "code",
       "message"
      ],
      "properties": {
       "code": {
        "type": "string",
        "example": "server_not_found"
       },
       "message": {
        "type": "string"
       },
       "details": {
        "type": "object",
        "additionalProperties": true,
        "description": "Extra data. Limit refusals add the LimitHint fields (`limit_exceeded: true`, `limit_key`, `limit`, `current`, `how_to_increase`, `support_ticket`, `hint`): account limits are raised with a support ticket. Validation errors (length, format) never carry the limit hint.",
        "x-description-tr": "Ek veri. Sınır retlerinde LimitHint alanları gelir: hesap sınırları destek talebiyle artırılır. Doğrulama hataları (uzunluk, biçim) sınır ipucu taşımaz."
       }
      }
     }
    }
   },
   "CreateServerResult": {
    "type": "object",
    "properties": {
     "server": {
      "$ref": "#/components/schemas/Server"
     },
     "order": {
      "type": "object",
      "properties": {
       "id": {
        "type": "integer"
       },
       "number": {
        "type": "string"
       },
       "status": {
        "type": "string"
       },
       "total": {
        "$ref": "#/components/schemas/Money"
       }
      }
     },
     "payment": {
      "type": "object",
      "properties": {
       "method": {
        "type": "string",
        "const": "balance"
       },
       "status": {
        "type": "string",
        "enum": [
         "paid",
         "failed",
         "unpaid"
        ]
       },
       "error": {
        "type": "string"
       }
      }
     },
     "invoice": {
      "$ref": "#/components/schemas/Invoice"
     },
     "end_customer_id": {
      "type": [
       "integer",
       "null"
      ]
     },
     "servers": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Server"
      },
      "description": "All servers of the order (2.21.0); `server` = the first one."
     },
     "quantity": {
      "type": "integer"
     }
    }
   },
   "Server": {
    "type": "object",
    "properties": {
     "id": {
      "type": "integer",
      "example": 47466
     },
     "name": {
      "type": "string",
      "description": "Product name"
     },
     "hostname": {
      "type": [
       "string",
       "null"
      ]
     },
     "status": {
      "type": "string",
      "enum": [
       "waiting",
       "inprocess",
       "active",
       "suspended",
       "cancelled"
      ],
      "description": "Billing / service status"
     },
     "product_id": {
      "type": "integer"
     },
     "plan": {
      "type": [
       "string",
       "null"
      ],
      "example": "standart-2-4"
     },
     "region": {
      "type": [
       "string",
       "null"
      ],
      "example": "eu-de-1"
     },
     "server_type": {
      "type": [
       "string",
       "null"
      ],
      "deprecated": true,
      "description": "Legacy type code (use plan)"
     },
     "location": {
      "type": [
       "string",
       "null"
      ],
      "deprecated": true,
      "description": "Legacy location code (use region)"
     },
     "ipv4": {
      "type": [
       "string",
       "null"
      ]
     },
     "ipv6": {
      "type": [
       "string",
       "null"
      ],
      "description": "IPv6 network (/64)"
     },
     "provisioned": {
      "type": "boolean",
      "description": "true once the machine exists"
     },
     "cycle": {
      "type": "string",
      "example": "monthly"
     },
     "price": {
      "$ref": "#/components/schemas/Money"
     },
     "auto_renew": {
      "type": "boolean"
     },
     "created_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "due_date": {
      "type": [
       "string",
       "null"
      ],
      "format": "date"
     },
     "cancellation": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "pending",
       "approved",
       null
      ]
     },
     "customer_id": {
      "type": [
       "integer",
       "null"
      ],
      "description": "Reseller end-customer (main key only)"
     },
     "product_line": {
      "type": "string",
      "enum": [
       "cloud_server",
       "vds"
      ]
     },
     "billing": {
      "type": "string",
      "enum": [
       "invoice",
       "prepaid"
      ],
      "description": "prepaid: `cycle` is `hourly`, `price.amount` is the hourly EUR price, `due_date` is null."
     }
    }
   },
   "Money": {
    "type": "object",
    "properties": {
     "amount": {
      "type": "number"
     },
     "currency": {
      "type": [
       "string",
       "null"
      ],
      "example": "TRY"
     }
    }
   },
   "Invoice": {
    "type": "object",
    "additionalProperties": true,
    "properties": {
     "invoice_id": {
      "type": "integer"
     },
     "number": {
      "type": "string"
     },
     "status": {
      "type": "string"
     },
     "state": {
      "type": "string",
      "enum": [
       "paid",
       "unpaid",
       "overdue",
       "refunded",
       "cancelled",
       "waiting"
      ]
     },
     "total": {
      "$ref": "#/components/schemas/Money"
     },
     "created_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date"
     },
     "paid_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date"
     },
     "due_date": {
      "type": [
       "string",
       "null"
      ],
      "format": "date"
     }
    }
   },
   "Quote": {
    "type": "object",
    "properties": {
     "product_id": {
      "type": "integer"
     },
     "cycle": {
      "type": "string"
     },
     "total": {
      "$ref": "#/components/schemas/Money"
     },
     "tax": {
      "$ref": "#/components/schemas/Money"
     },
     "balance": {
      "$ref": "#/components/schemas/Money"
     }
    }
   },
   "CreateServerRequest": {
    "type": "object",
    "properties": {
     "product_id": {
      "type": "integer",
      "description": "From GET /cloud/products (or give plan + region)"
     },
     "plan": {
      "type": "string",
      "example": "standart-2-4",
      "description": "Plan name from GET /cloud/products"
     },
     "region": {
      "type": "string",
      "example": "eu-de-1",
      "description": "Region from GET /cloud/locations"
     },
     "server_type": {
      "type": "string",
      "deprecated": true,
      "description": "Deprecated alias of plan"
     },
     "location": {
      "type": "string",
      "deprecated": true,
      "description": "Deprecated alias of region"
     },
     "cycle": {
      "type": "string",
      "default": "monthly",
      "enum": [
       "monthly",
       "quarterly",
       "semiannually",
       "annually"
      ]
     },
     "image": {
      "type": "string",
      "example": "debian-12"
     },
     "ssh_keys": {
      "type": "array",
      "items": {
       "type": "integer"
      },
      "maxItems": 10,
      "description": "Ids from /cloud/ssh-keys. Global regions: one key, installed at creation. tr-ist-1: up to 10 keys, installed for root at creation through cloud-init (`ssh_keys` on GET /cloud/servers/{id} lists their fingerprints and key ids)."
     },
     "hostname": {
      "type": "string",
      "example": "web-1.example.com",
      "description": " In a batch `{n}` is replaced with 1..N (e.g. `web-{n}.example.com`); without `{n}` `-1`, `-2`... is appended."
     },
     "user_data": {
      "type": "string",
      "maxLength": 32768,
      "description": "cloud-init user data: `#cloud-config` or a `#!` script, max 32 KiB. Accepted in every region (tr-ist-1 since 2.14.0). Ignored when a one-click application is chosen on the product.",
      "x-description-tr": "cloud-init kullanıcı verisi: `#cloud-config` ya da `#!` betiği, en çok 32 KiB. Tüm bölgelerde (tr-ist-1'de 2.14.0'dan beri) kabul edilir. Üründe tek tıkla uygulama seçilirse kullanılmaz."
     },
     "dry_run": {
      "type": "boolean",
      "default": false
     },
     "confirm": {
      "type": "boolean",
      "description": "Must be true to place a paid order"
     },
     "end_customer_id": {
      "type": "integer",
      "description": "Reseller main key: assign the new server to this end-customer"
     },
     "project_id": {
      "type": "integer",
      "minimum": 0,
      "description": "Optional project for the new service(s) (0 = default project). Checked before the order: unknown id = 404 `project_not_found` (GyroAPI order endpoints) or 422 `checkout_blocked` with `[project_not_found]` (POST /orders)."
     },
     "quantity": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "default": 1,
      "description": "Create N identical servers in ONE order (default 1). Upper limit per request: 25 by default (operator setting / per-account override; 422 invalid_quantity above it). The server cap and the prepaid 24-hour balance rule count all N."
     },
     "name": {
      "type": "string",
      "maxLength": 60,
      "example": "gyro-web-{n}",
      "description": "Service name (nickname, shown in the panel, console and API as `nickname`). `{n}` is replaced with 1..N in a batch; without `{n}` a batch appends `-1`, `-2`..."
     },
     "tags": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "oneOf": [
        {
         "type": "string"
        },
        {
         "type": "object",
         "properties": {
          "name": {
           "type": "string"
          },
          "color": {
           "type": "string"
          }
         }
        }
       ]
      },
      "description": "Labels for every new server (max 10, 24 characters each)."
     },
     "app": {
      "type": "string",
      "description": "One-click app id from GET /cloud/apps, installed when the server is up (products that offer apps). Not with `user_data`."
     },
     "app_domain": {
      "type": "string",
      "description": "Domain of the app (its A record must point to the new server); quantity 1 only."
     }
    }
   }
  },
  "responses": {
   "Conflict": {
    "description": "State conflict (`server_locked`, `not_actionable`, `ssh_key_exists`, `server_limit`, `idempotency_in_flight`)",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "Forbidden": {
    "description": "Key lacks the scope, IP not allowed, or feature disabled",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "PaymentRequired": {
    "description": "Account balance does not cover the order (`insufficient_balance`)",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "Unauthorized": {
    "description": "Missing, invalid or revoked key",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   },
   "BadRequest": {
    "description": "Malformed request (e.g. missing Idempotency-Key)",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  },
  "parameters": {
   "IdempotencyKey": {
    "name": "Idempotency-Key",
    "in": "header",
    "required": true,
    "schema": {
     "type": "string",
     "minLength": 8,
     "maxLength": 191
    },
    "description": "Unique per logical request (e.g. a UUID). Replays within 24 hours return the stored answer."
   }
  }
 },
 "security": [
  {
   "bearerAuth": []
  }
 ]
}