{
  "openapi": "3.1.0",
  "info": {
    "title": "x333e3 Customer API",
    "version": "1",
    "description": "Implemented customer endpoints. Amounts are integer USD cents. POST performs real operations."
  },
  "servers": [
    {
      "url": "https://x333e3.xyz/api/v1"
    }
  ],
  "paths": {
    "/plans": {
      "get": {
        "summary": "Тарифы и наличие",
        "description": "Актуальные конфигурации, цены в центах и число доступных серверов по локациям.",
        "operationId": "plans",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "servers:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "name": "Nano",
                      "cpu": 4,
                      "ram_gb": 8,
                      "disk_gb": 100,
                      "price_cents": 1100,
                      "locations": [
                        {
                          "code": "nl",
                          "label": "Нидерланды",
                          "available": 2
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/servers": {
      "get": {
        "summary": "Список серверов",
        "description": "Сервера рабочего пространства токена. Учитываются права участника и список доступных ему серверов.",
        "operationId": "servers",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "servers:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "0123456789abcdef0123456789abcdef",
                      "name": "web-01",
                      "state": "provisioned",
                      "ip": "192.0.2.10",
                      "configuration": "4 vCPU / 8 GB RAM / 100 GB NVMe",
                      "location": "Нидерланды",
                      "os": "Ubuntu 24.04",
                      "renewal_price_cents": 1100,
                      "paid_until": 1795000000,
                      "auto_renew": true
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/servers/{hash}": {
      "get": {
        "summary": "Данные сервера",
        "description": "Конфигурация, адрес, состояние, срок оплаты и автопродление. Пароли и реквизиты в ответ не входят.",
        "operationId": "server",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "servers:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "data": {
                    "id": "0123456789abcdef0123456789abcdef",
                    "name": "web-01",
                    "state": "provisioned",
                    "ip": "192.0.2.10",
                    "configuration": "4 vCPU / 8 GB RAM / 100 GB NVMe",
                    "location": "Нидерланды",
                    "os": "Ubuntu 24.04",
                    "renewal_price_cents": 1100,
                    "paid_until": 1795000000,
                    "auto_renew": true
                  }
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          }
        ]
      }
    },
    "/operations": {
      "get": {
        "summary": "Состояние операций",
        "description": "До 100 последних операций. Сопоставьте id с operation_id принятой команды и проверяйте state.",
        "operationId": "operations",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "operations:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 42,
                      "server": "0123456789abcdef0123456789abcdef",
                      "action": "restart",
                      "action_label": "Перезапуск",
                      "state": "queued",
                      "label": "В очереди",
                      "phase": "В очереди",
                      "created": "2026-10-26 10:00:00",
                      "updated": "2026-10-26 10:00:00",
                      "error": ""
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/billing": {
      "get": {
        "summary": "История расходов",
        "description": "До 5000 операций за выбранный месяц. amount_cents — целые центы USD: положительное значение означает пополнение, отрицательное — списание.",
        "operationId": "billing",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "billing:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 7,
                      "created": "2026-10-26 10:00:00",
                      "amount_cents": -1100,
                      "description": "Продление сервера",
                      "server": "0123456789abcdef0123456789abcdef",
                      "project": "Без проекта"
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "month",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^20[0-9]{2}-(0[1-9]|1[0-2])$"
            }
          }
        ]
      }
    },
    "/cart": {
      "post": {
        "summary": "Добавить в корзину",
        "description": "Добавляет позицию или заменяет её количество для сочетания тарифа и локации. Само добавление не оформляет заказ.",
        "operationId": "cart",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "orders:write",
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "data": {
                    "cart": [
                      {
                        "plan_id": 1,
                        "location": "nl",
                        "quantity": 1
                      }
                    ]
                  }
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 100,
              "pattern": "^[A-Za-z0-9:_-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "plan_id",
                  "location"
                ],
                "properties": {
                  "plan_id": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "location": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10,
                    "default": 1
                  },
                  "project_id": {
                    "type": "integer",
                    "minimum": 0,
                    "default": 0
                  }
                }
              },
              "example": {
                "plan_id": 1,
                "location": "nl",
                "quantity": 1
              }
            }
          }
        }
      }
    },
    "/checkout": {
      "post": {
        "summary": "Оформить заказ",
        "description": "Оформляет всю корзину с баланса. Наличие и стоимость проверяются заново. При нехватке серверов корзина не списывается частично.",
        "operationId": "checkout",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "orders:write",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "data": {
                    "kind": "checkout",
                    "servers": [
                      {
                        "server": "0123456789abcdef0123456789abcdef",
                        "operation_id": 42
                      }
                    ],
                    "amount_cents": 1100
                  }
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 100,
              "pattern": "^[A-Za-z0-9:_-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              },
              "example": {}
            }
          }
        }
      }
    },
    "/servers/{hash}/start": {
      "post": {
        "summary": "Включить сервер",
        "description": "Ставит включение сервера в очередь. Принятая команда ещё не означает завершённое включение.",
        "operationId": "start",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "servers:power",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "data": {
                    "operation_id": 42,
                    "state": "queued"
                  }
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 100,
              "pattern": "^[A-Za-z0-9:_-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              },
              "example": {}
            }
          }
        }
      }
    },
    "/servers/{hash}/stop": {
      "post": {
        "summary": "Выключить сервер",
        "description": "Ставит выключение в очередь. Команда влияет на работу реального сервера.",
        "operationId": "stop",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "servers:power",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "data": {
                    "operation_id": 42,
                    "state": "queued"
                  }
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 100,
              "pattern": "^[A-Za-z0-9:_-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              },
              "example": {}
            }
          }
        }
      }
    },
    "/servers/{hash}/restart": {
      "post": {
        "summary": "Перезапустить сервер",
        "description": "Ставит перезапуск в очередь. Отслеживайте результат через GET /operations.",
        "operationId": "restart",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "servers:power",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "data": {
                    "operation_id": 42,
                    "state": "queued"
                  }
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 100,
              "pattern": "^[A-Za-z0-9:_-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              },
              "example": {}
            }
          }
        }
      }
    },
    "/servers/{hash}/renew": {
      "post": {
        "summary": "Продлить на 30 дней",
        "description": "Продлевает сервер с баланса рабочего пространства. Возвращает новый срок оплаты и списанную сумму в центах. Продление выполняется сразу; отдельная операция в очереди не создаётся.",
        "operationId": "renew",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scope": "servers:renew",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  }
                },
                "example": {
                  "data": {
                    "paid_until": 1797592000,
                    "amount_cents": 1100
                  }
                }
              }
            }
          },
          "default": {
            "description": "API error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-f0-9]{32}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 100,
              "pattern": "^[A-Za-z0-9:_-]+$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              },
              "example": {}
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Create a scoped token in account settings."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "message",
              "code"
            ],
            "properties": {
              "message": {
                "type": "string"
              },
              "code": {
                "type": "integer"
              }
            }
          }
        }
      }
    }
  }
}