{
  "openapi": "3.0.3",
  "info": {
    "title": "AiST Feature Flags",
    "version": "1.2.0",
    "description": "Версии приложения AiST с булевым статусом. Версия создаётся в панели и получает UUID; приложение по этому UUID узнаёт, включена ли она и на каком домене работает. UUID неизменен, версии не удаляются — название, домен, описание и статус можно править.\n\nАвторизация: ключ передаётся в заголовке `Authorization: Bearer <ключ>` или `X-Api-Key`. Управление версиями — ключ администратора (ADMIN_KEY), проверка статуса — клиентский ключ (CLIENT_KEY).\n\nВеб-панель вместо ключа в каждом запросе использует сессию: POST /api/session ставит HttpOnly-куку aff_session на 30 дней. Кука подписана ADMIN_KEY, на сервере ничего не хранится — смена ключа завершает все сессии."
  },
  "servers": [
    {
      "url": "https://aist-feature-flags-production-4fg9cbrc.hosting.neobash.ru"
    }
  ],
  "components": {
    "securitySchemes": {
      "adminKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "ADMIN_KEY — панель и управление версиями"
      },
      "clientKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "CLIENT_KEY — проверка статуса из приложения"
      },
      "session": {
        "type": "apiKey",
        "in": "cookie",
        "name": "aff_session",
        "description": "Кука панели, выдаётся POST /api/session на 30 дней"
      }
    },
    "schemas": {
      "Version": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Идентификатор версии, генерируется сервисом и никогда не меняется"
          },
          "name": {
            "type": "string",
            "example": "2.2.0 (57)"
          },
          "description": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean",
            "description": "Включена ли версия"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "domain": {
            "type": "string",
            "description": "Домен, к которому обращается версия. По умолчанию chat.aist-ai.com; протокол и хвостовые слеши срезаются.",
            "example": "chat.aist-ai.com"
          }
        },
        "required": [
          "uuid",
          "name",
          "domain",
          "enabled"
        ]
      },
      "Status": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "domain": {
            "type": "string",
            "example": "chat.aist-ai.com"
          }
        },
        "required": [
          "uuid",
          "enabled",
          "domain"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/api/versions/{uuid}/status": {
      "get": {
        "summary": "Статус версии (для приложения)",
        "description": "Единственный эндпоинт, нужный приложению: булево enabled и домен версии.",
        "security": [
          {
            "clientKey": []
          }
        ],
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Статус версии",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                },
                "example": {
                  "uuid": "0d8f2f4e-2c9e-4a0f-9a0b-1f2c3d4e5f60",
                  "enabled": true,
                  "domain": "chat.aist-ai.com"
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Версия не найдена",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/versions": {
      "get": {
        "summary": "Список версий",
        "security": [
          {
            "adminKey": []
          },
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Все версии, новые сверху",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Version"
                      }
                    },
                    "default_domain": {
                      "type": "string",
                      "description": "Домен, подставляемый новым версиям"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ"
          }
        }
      },
      "post": {
        "summary": "Создать версию",
        "description": "UUID генерируется сервисом. Без поля enabled версия создаётся выключенной, без domain — с chat.aist-ai.com.",
        "security": [
          {
            "adminKey": []
          },
          {
            "session": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean",
                    "default": false
                  },
                  "domain": {
                    "type": "string",
                    "default": "chat.aist-ai.com",
                    "description": "Необязателен: без него подставляется chat.aist-ai.com"
                  }
                }
              },
              "example": {
                "name": "2.2.0 (57)",
                "description": "Локализация на 6 языков",
                "domain": "chat.aist-ai.com",
                "enabled": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Созданная версия",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Version"
                }
              }
            }
          },
          "400": {
            "description": "Не указано название"
          },
          "401": {
            "description": "Неверный ключ"
          }
        }
      }
    },
    "/api/versions/{uuid}": {
      "patch": {
        "summary": "Изменить версию",
        "description": "Передаются только изменяемые поля, хотя бы одно: enabled, name, description, domain. UUID изменить нельзя — он выдан клиентам. Пустой domain возвращает значение по умолчанию.",
        "security": [
          {
            "adminKey": []
          },
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "minProperties": 1,
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "domain": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "domain": "beta.aist-ai.com",
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Обновлённая версия",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Version"
                }
              }
            }
          },
          "400": {
            "description": "Нечего менять, пустое название или неверный тип enabled"
          },
          "401": {
            "description": "Неверный ключ"
          },
          "404": {
            "description": "Версия не найдена"
          }
        }
      }
    },
    "/health": {
      "get": {
        "summary": "Проверка живости",
        "responses": {
          "200": {
            "description": "Сервис жив",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "versions": 3
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "summary": "Эта документация в JSON",
        "responses": {
          "200": {
            "description": "OpenAPI-описание"
          }
        }
      }
    },
    "/api/session": {
      "post": {
        "summary": "Войти в панель",
        "description": "Проверяет ADMIN_KEY и ставит куку aff_session на 30 дней.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "description": "ADMIN_KEY"
                  }
                }
              },
              "example": {
                "key": "<ADMIN_KEY>"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Сессия создана",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "expires_at": "2026-09-22T14:07:50.231Z"
                }
              }
            }
          },
          "401": {
            "description": "Неверный ключ"
          }
        }
      },
      "get": {
        "summary": "Жива ли сессия",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Состояние сессии",
            "content": {
              "application/json": {
                "example": {
                  "authorized": true
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Выйти",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Кука очищена",
            "content": {
              "application/json": {
                "example": {
                  "ok": true
                }
              }
            }
          }
        }
      }
    }
  }
}