{
  "openapi": "3.1.0",
  "info": {
    "title": "ZeroGuard Personal & BYOK API",
    "version": "1.3.0",
    "description": "Личный подключённый API. Доступ определяется источником; продажи и коммерческие тарифы этим документом не включаются."
  },
  "servers": [
    {
      "url": "https://159-194-242-109.sslip.io/llm-api/v1",
      "description": "Публичный HTTPS-маршрут"
    },
    {
      "url": "http://127.0.0.1:3003/v1",
      "description": "Локальный API"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyAuth": []
    }
  ],
  "components": {
    "schemas": {
      "Message": {
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "system",
              "developer",
              "user",
              "assistant",
              "tool"
            ]
          },
          "content": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "tool_calls": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "tool_call_id": {
            "type": "string"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      },
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    }
  },
  "paths": {
    "/models": {
      "get": {
        "operationId": "listModels",
        "summary": "Доступные модели и capabilities",
        "responses": {
          "200": {
            "description": "Модели для этого ключа"
          }
        }
      }
    },
    "/chat/completions": {
      "post": {
        "operationId": "createChatCompletion",
        "summary": "createChatCompletion",
        "description": "JSON или SSE. X-Request-ID совпадает с error.request_id. В личном маршруте лимит вывода advisory; проверяйте фактический usage. Не повторяйте автоматически запрос после частичного ответа.",
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "Сохраняет успешный непотоковый ответ; повтор с тем же телом не запускает новую генерацию."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "model",
                  "messages"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "example": "gpt-6-sol"
                  },
                  "stream": {
                    "type": "boolean",
                    "default": false
                  },
                  "messages": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Message"
                    }
                  },
                  "max_tokens": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "stream_options": {
                    "type": "object",
                    "properties": {
                      "include_usage": {
                        "type": "boolean"
                      }
                    }
                  },
                  "tools": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Завершённый ответ или поток SSE",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Лимит или очередь; соблюдайте Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Источник не ответил до тайм-аута; запрос мог быть выполнен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/responses": {
      "post": {
        "operationId": "createResponse",
        "summary": "createResponse",
        "description": "JSON или SSE. X-Request-ID совпадает с error.request_id. В личном маршруте лимит вывода advisory; проверяйте фактический usage. Не повторяйте автоматически запрос после частичного ответа.",
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "Сохраняет успешный непотоковый ответ; повтор с тем же телом не запускает новую генерацию."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "model",
                  "input"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "example": "gpt-6-sol"
                  },
                  "stream": {
                    "type": "boolean",
                    "default": false
                  },
                  "input": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      }
                    ]
                  },
                  "store": {
                    "type": "boolean"
                  },
                  "previous_response_id": {
                    "type": "string"
                  },
                  "max_output_tokens": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "reasoning": {
                    "type": "object"
                  },
                  "tools": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Завершённый ответ или поток SSE",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Лимит или очередь; соблюдайте Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Источник не ответил до тайм-аута; запрос мог быть выполнен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/messages": {
      "post": {
        "operationId": "createMessage",
        "summary": "createMessage",
        "description": "JSON или SSE. X-Request-ID совпадает с error.request_id. В личном маршруте лимит вывода advisory; проверяйте фактический usage. Не повторяйте автоматически запрос после частичного ответа.",
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "Сохраняет успешный непотоковый ответ; повтор с тем же телом не запускает новую генерацию."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "model",
                  "messages",
                  "max_tokens"
                ],
                "properties": {
                  "model": {
                    "type": "string",
                    "example": "gpt-6-sol"
                  },
                  "stream": {
                    "type": "boolean",
                    "default": false
                  },
                  "messages": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Message"
                    }
                  },
                  "system": {
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      }
                    ]
                  },
                  "max_tokens": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "tools": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Завершённый ответ или поток SSE",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Лимит или очередь; соблюдайте Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Ошибка запроса или источника",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "504": {
            "description": "Источник не ответил до тайм-аута; запрос мог быть выполнен",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Личный статус, очередь и метаданные запросов",
        "responses": {
          "200": {
            "description": "Без ключей и содержимого диалогов"
          },
          "401": {
            "description": "Нужен ключ"
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Статистика личного ключа и последние 50 запросов",
        "responses": {
          "200": {
            "description": "Фактические токены и статусы"
          },
          "401": {
            "description": "Нужен ключ"
          }
        }
      }
    },
    "/requests": {
      "get": {
        "operationId": "getRequests",
        "summary": "История текущего ключа с фильтрами и пагинацией",
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 100000
            }
          },
          {
            "in": "query",
            "name": "state",
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "search",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "history, total, limit, offset, has_more; тексты диалогов не возвращаются"
          },
          "400": {
            "description": "Некорректные фильтры"
          },
          "401": {
            "description": "Нужен ключ"
          }
        }
      }
    },
    "/keys": {
      "get": {
        "operationId": "listKeys",
        "summary": "Ключи владельца; требуется основной ключ",
        "responses": {
          "200": {
            "description": "Префиксы и ограничения, без полных ключей"
          },
          "403": {
            "description": "Нужен основной ключ"
          }
        }
      },
      "post": {
        "operationId": "createKey",
        "summary": "Создать личный ключ для IDE; требуется основной ключ",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "rpm": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 30
                  },
                  "models": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string"
                    },
                    "description": "Разрешённые подключённые модели. По умолчанию текущий каталог; будущие модели автоматически не добавляются."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "api_key показывается один раз"
          },
          "403": {
            "description": "Нужен основной ключ"
          },
          "409": {
            "description": "Лимит 20 активных ключей"
          }
        }
      }
    },
    "/keys/{id}": {
      "delete": {
        "operationId": "revokeKey",
        "summary": "Отключить ключ IDE; основной ключ защищён",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ключ отключён"
          },
          "403": {
            "description": "Нужен основной ключ"
          },
          "404": {
            "description": "Ключ не найден"
          },
          "409": {
            "description": "Основной ключ защищён"
          }
        }
      },
      "post": {
        "operationId": "updateKeyLimits",
        "summary": "Изменить RPM и список моделей IDE-ключа; основной ключ",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rpm": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 30
                  },
                  "models": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "updated=true"
          },
          "403": {
            "description": "Нужен основной ключ"
          },
          "409": {
            "description": "Основной ключ защищён"
          }
        }
      }
    },
    "/providers": {
      "get": {
        "operationId": "listProviders",
        "summary": "Профили и состояние поставщиков; основной ключ",
        "responses": {
          "200": {
            "description": "Публичные профили, configured/verified, без ключей"
          },
          "403": {
            "description": "Нужен основной ключ"
          }
        }
      },
      "post": {
        "operationId": "connectProvider",
        "summary": "Сохранить PAYG ключ и проверить официальный каталог; основной ключ",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "api_key"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "api_key": {
                    "type": "string",
                    "writeOnly": true,
                    "minLength": 8,
                    "maxLength": 4096
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "saved, verified, test_required; без генерации"
          },
          "401": {
            "description": "Поставщик не принял ключ"
          },
          "409": {
            "description": "Запросы ещё выполняются"
          }
        }
      }
    },
    "/providers/test": {
      "post": {
        "operationId": "testProvider",
        "summary": "Один платный тест выбранной модели; основной ключ",
        "description": "Списание у поставщика. До 128 токенов вывода, без автоматического повтора. Нужен accept_paid_test=true.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "model",
                  "accept_paid_test"
                ],
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  },
                  "accept_paid_test": {
                    "type": "boolean",
                    "const": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Модель и usage подтверждены, tools отдельно не тестировались"
          },
          "400": {
            "description": "Нет согласия или настройки"
          },
          "502": {
            "description": "Тест не подтвердил модель"
          }
        }
      }
    },
    "/providers/{id}": {
      "delete": {
        "operationId": "disconnectProvider",
        "summary": "Удалить сохранённый ключ и выключить модели; основной ключ",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "removed=true"
          },
          "403": {
            "description": "Нужен основной ключ"
          }
        }
      }
    },
    "/preferences": {
      "get": {
        "operationId": "getPreferences",
        "summary": "Свои промпты и настройки; основной ключ",
        "responses": {
          "200": {
            "description": "settings, prompts"
          },
          "403": {
            "description": "Нужен основной ключ"
          }
        }
      },
      "post": {
        "operationId": "savePreferences",
        "summary": "Настроить уведомления или системную инструкцию семейства",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notifications": {
                    "type": "boolean"
                  },
                  "prompt": {
                    "type": "object",
                    "properties": {
                      "family": {
                        "type": "string"
                      },
                      "text": {
                        "type": "string",
                        "maxLength": 20000
                      },
                      "enabled": {
                        "type": "boolean"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Сохранённые настройки"
          },
          "403": {
            "description": "Нужен основной ключ"
          }
        }
      }
    },
    "/finance": {
      "get": {
        "operationId": "getFinance",
        "summary": "Списания шлюза для текущего ключа",
        "description": "BYOK оплачивается у поставщика; личный шлюз не включает продажу пакетов.",
        "responses": {
          "200": {
            "description": "gateway_charged_usd, requests, ledger последних 100 операций; без чужих ключей"
          }
        }
      }
    }
  }
}