openapi: 3.1.1 info: title: t-cat-cashback API version: 0.5.0 description: | Backend + ML API для продукта управления кэшбэком. ## Принципы - **Auth:** JWT Bearer (`Authorization: Bearer `) - **Ошибки:** `{ "code": "UPPER_SNAKE_CASE", "message": "..." }` - **Rate limiting:** 429 Too Many Requests ## Сервисы | Сервис | Стек | Порт | Назначение | |--------|------|------|------------| | Backend | Rust / Axum | 8000 | Основной API | | ML | Python / FastAPI | 8005 | Персонализация кэшбэка | ## Возможности v0.5.0 **Targeting и правила** - DSL targeting engine — парсер и evaluator правил таргетинга - CITY / COUNTRY в профиле пользователя **Администрирование** - Batch approve/reject заявок администраторов - Статистика заявок (requests/stats) - Статистика платформы для администраторов **Кэшбэк** - Выбор категорий кэшбэка и отслеживание прогресса - ML predict с 3-retry + math fallback **Инфраструктура** - Batch query collector — пакетная запись при высокой нагрузке - Метрики с авторизацией (Prometheus + UI дашборд) - Улучшенное кэширование (admin requests, stats) - 12 новых error codes - Кастомный MD-to-HTML движок документации - T-Bank dark theme для metrics UI и debug UI - /docs static file serving license: name: Proprietary servers: - url: http://158.160.83.199:8000 description: Backend — Production - url: http://backend:8000 description: Backend (docker internal) - url: https://example.com:8000 description: Backend — Contest Pages security: - bearerAuth: [] tags: - name: Meta description: Health-check и метаданные сервиса - name: Auth description: Аутентификация, регистрация, сессии - name: Me description: Профиль текущего пользователя - name: Progress description: Прогресс пользователя — выбор категорий, колесо фортуны, траты - name: Cashback description: Выбор кэшбэка, статистика, прогнозы, история транзакций - name: Users description: Управление пользователями (admin/system) - name: Admins description: Управление администраторами (system_permission-only) - name: AdminRequests description: Заявки на получение роли администратора - name: Categories description: Категории кэшбэка (CRUD, бюджеты, правила таргетинга) - name: Rules description: Наборы правил таргетинга (DSL) и привязка к категориям - name: Settings description: Глобальные настройки приложения - name: AdminStats description: Статистика платформы для администраторов (бюджеты, траты, пользователи) - name: Observability description: Мониторинг и метрики - name: Metrics description: Prometheus метрики и дашборд (требуется авторизация admin/system) - name: ML description: ML-сервис персонализации кэшбэка (score, profit, notify, health, metrics, drift, reload) - name: Debug description: DEV-only endpoints для отладки и тестирования paths: /health: get: tags: - Meta summary: Health-check (JSON для API, HTML для браузера) description: | Content negotiation: если `Accept: text/html` или User-Agent — браузер, возвращается AMOLED HTML-страница с live-clock, DB latency и uptime. Иначе — JSON. operationId: healthCheck security: [] responses: '200': description: OK — все проверки пройдены content: application/json: schema: $ref: '#/components/schemas/HealthResponse' text/html: schema: type: string description: AMOLED HTML dashboard (браузер) '503': description: Service Unavailable — БД недоступна content: application/json: schema: $ref: '#/components/schemas/HealthResponse' /metrics: get: tags: - Observability summary: Prometheus metrics operationId: metricsGet security: [] responses: '200': description: Prometheus text format content: text/plain: schema: type: string '500': description: Internal Server Error /api/meta: get: tags: - Meta summary: Метаданные сервиса (JSON для API, HTML для браузера) operationId: metaGet security: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MetaResponse' text/html: schema: type: string description: AMOLED HTML dashboard (браузер) '500': description: Internal Server Error /api/v1/auth/login: post: tags: - Auth summary: Логин (bearer) operationId: authLogin security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AuthLoginRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AuthSessionResponse' '403': description: Forbidden by policy content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/auth/register: post: tags: - Auth summary: Регистрация (user сразу активен, admin создаёт admin request) operationId: authRegister security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AuthRegisterRequest' responses: '201': description: Created (role=user) content: application/json: schema: $ref: '#/components/schemas/RegisterCreatedResponse' '202': description: Accepted (role=admin -> pending approval) content: application/json: schema: $ref: '#/components/schemas/RegisterPendingApprovalResponse' '409': description: Conflict content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/auth/session: get: tags: - Auth summary: Текущая сессия operationId: authSessionGet responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AuthSessionResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/auth/logout: post: tags: - Auth summary: Логаут operationId: authLogout responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/LogoutResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/auth/refresh: post: tags: - Auth summary: Обновление токена operationId: authRefresh responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AuthSessionResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/me: get: tags: - Me summary: Профиль текущего пользователя operationId: meGet responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MeGetResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' patch: tags: - Me summary: Обновить профиль (self) description: | Пользователь может обновить login, name, profile, to_be_deleted. Нельзя менять role и system_permission через этот endpoint. operationId: mePatch requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MeUpdateRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MeUpdateResponse' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/ApiError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '409': description: Conflict (login уже существует) content: application/json: schema: $ref: '#/components/schemas/ApiError' delete: tags: - Me summary: Удалить себя (только non-admin) description: | Self-delete разрешён только для non-admin пользователей. Для админов используйте `DELETE /api/v1/admins/me`. operationId: meDelete responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OkResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/users: get: tags: - Users summary: Список пользователей (admin/system) operationId: usersList parameters: - name: limit in: query schema: type: integer minimum: 1 maximum: 200 default: 50 - name: offset in: query schema: type: integer minimum: 0 default: 0 - name: search in: query schema: type: string responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/UsersListResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' post: tags: - Users summary: Upsert пользователя по login (admin/system) operationId: usersUpsert requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserUpsertRequest' responses: '200': description: Updated content: application/json: schema: $ref: '#/components/schemas/UserGetResponse' '201': description: Created content: application/json: schema: $ref: '#/components/schemas/UserGetResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '409': description: Conflict content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/users/{user_id}: parameters: - name: user_id in: path required: true schema: type: string description: UUID или "me" get: tags: - Users summary: Получить пользователя (admin/system) operationId: usersGet responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/UserGetResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' patch: tags: - Users summary: Обновить пользователя (self/admin/system) operationId: usersPatch requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserPatchRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/UserGetResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' delete: tags: - Users summary: Удалить пользователя (admin/system; self-delete запрещён тут) operationId: usersDelete responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OkResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins: get: tags: - Admins summary: Список админов (system_permission-only) operationId: adminsList parameters: - name: limit in: query schema: type: integer minimum: 1 maximum: 200 default: 50 - name: offset in: query schema: type: integer minimum: 0 default: 0 - name: search in: query schema: type: string responses: '200': description: OK content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/AdminListItem' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' post: tags: - Admins summary: Создать админа (system_permission-only) operationId: adminsCreate requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AdminCreateRequest' responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/AdminCreateResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '409': description: Conflict content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/me: delete: tags: - Admins summary: Удалить себя (admin/system) с safety rule operationId: adminsDeleteMe responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OkResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/{admin_id}: parameters: - name: admin_id in: path required: true schema: type: string format: uuid delete: tags: - Admins summary: Удалить админа (system_permission-only) operationId: adminsDelete responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OkResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/requests: get: tags: - AdminRequests summary: Список заявок в админы (system_permission-only) operationId: adminRequestsList parameters: - name: limit in: query schema: type: integer minimum: 1 maximum: 200 default: 50 - name: offset in: query schema: type: integer minimum: 0 default: 0 - name: status in: query schema: type: string description: APPROVED|PENDING_APPROVAL|REJECTED - name: search in: query schema: type: string responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AdminRequestsListResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/requests/{request_id}: parameters: - name: request_id in: path required: true schema: type: string format: uuid get: tags: - AdminRequests summary: Получить заявку в админы operationId: adminRequestsGet responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AdminRequestGetResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' delete: tags: - AdminRequests summary: Удалить заявку в админы operationId: adminRequestsDelete responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OkResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/requests/{request_id}/approve: parameters: - name: request_id in: path required: true schema: type: string format: uuid post: tags: - AdminRequests summary: Утвердить заявку в админы operationId: adminRequestsApprove requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/AdminRequestReviewRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AdminRequestApproveResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' '409': description: Invalid state content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/requests/{request_id}/reject: parameters: - name: request_id in: path required: true schema: type: string format: uuid post: tags: - AdminRequests summary: Отклонить заявку в админы operationId: adminRequestsReject requestBody: required: false content: application/json: schema: $ref: '#/components/schemas/AdminRequestReviewRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AdminRequestRejectResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' '409': description: Invalid state content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/requests/stats: get: tags: - AdminRequests summary: Статистика заявок operationId: adminRequestsStats description: | Возвращает агрегированную статистику по заявкам на роль администратора: общее количество, pending, approved, rejected. Требуется admin или system_permission. security: - bearerAuth: [] responses: '200': description: Статистика заявок content: application/json: schema: $ref: '#/components/schemas/AdminRequestsStatsResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '429': description: Too many requests content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/requests/batch-approve: post: tags: - AdminRequests summary: Пакетное одобрение заявок operationId: adminRequestsBatchApprove description: | Одобряет несколько заявок на роль администратора за один запрос. Требуется admin или system_permission. security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchOperationRequest' responses: '200': description: Результат пакетного одобрения content: application/json: schema: $ref: '#/components/schemas/BatchOperationResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '429': description: Too many requests content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/requests/batch-reject: post: tags: - AdminRequests summary: Пакетное отклонение заявок operationId: adminRequestsBatchReject description: | Отклоняет несколько заявок на роль администратора за один запрос. Требуется admin или system_permission. security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchOperationRequest' responses: '200': description: Результат пакетного отклонения content: application/json: schema: $ref: '#/components/schemas/BatchOperationResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '429': description: Too many requests content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/admins/stats: get: tags: - AdminStats summary: Общая статистика платформы для администраторов description: | Глобальная статистика: сколько пользователей, категорий, транзакций, общая сумма кэшбэка, бюджет, и т.д. operationId: adminStats security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AdminStatsResponse' '403': $ref: '#/components/responses/ApiError' /api/v1/admins/stats/budget: get: tags: - AdminStats summary: Статистика бюджетов и расходов operationId: adminStatsBudget security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AdminBudgetStatsResponse' '403': $ref: '#/components/responses/ApiError' /api/v1/admins/stats/users: get: tags: - AdminStats summary: Статистика пользователей operationId: adminStatsUsers security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/AdminUserStatsResponse' '403': $ref: '#/components/responses/ApiError' /api/v1/categories: get: tags: - Categories summary: Список категорий (admin/system) operationId: categoriesList parameters: - name: limit in: query schema: type: integer minimum: 1 maximum: 200 default: 50 - name: offset in: query schema: type: integer minimum: 0 default: 0 - name: status in: query schema: type: string description: DRAFT|ACTIVE|DISABLED - name: search in: query schema: type: string responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CategoriesListResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' post: tags: - Categories summary: Создать категорию (admin/system) operationId: categoriesCreate requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CategoryUpsertRequest' responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/CategoryUpsertResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/categories/{category_id}: parameters: - name: category_id in: path required: true schema: type: string format: uuid get: tags: - Categories summary: Получить категорию operationId: categoriesGet responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CategoryGetResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' put: tags: - Categories summary: Обновить категорию operationId: categoriesUpdate requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CategoryUpsertRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CategoryUpsertResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' delete: tags: - Categories summary: Удалить категорию operationId: categoriesDelete responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OkResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/rules: get: tags: - Rules summary: Список наборов правил operationId: rulesList security: - bearerAuth: [] parameters: - name: limit in: query schema: type: integer default: 50 minimum: 1 maximum: 200 - name: offset in: query schema: type: integer default: 0 minimum: 0 - name: search in: query schema: type: string description: Поиск по имени/описанию responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/RuleSetsListResponse' '403': $ref: '#/components/responses/ApiError' post: tags: - Rules summary: Создать набор правил operationId: rulesCreate security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RuleSetUpsertRequest' responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/RuleSetUpsertResponse' '400': $ref: '#/components/responses/ApiError' '403': $ref: '#/components/responses/ApiError' /api/v1/rules/{rule_set_id}: parameters: - name: rule_set_id in: path required: true schema: type: string format: uuid get: tags: - Rules summary: Получить набор правил operationId: rulesGet security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/RuleSetGetResponse' '404': $ref: '#/components/responses/ApiError' put: tags: - Rules summary: Обновить набор правил operationId: rulesUpdate security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RuleSetUpsertRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/RuleSetUpsertResponse' '404': $ref: '#/components/responses/ApiError' delete: tags: - Rules summary: Удалить набор правил operationId: rulesDelete security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/OkResponse' '404': $ref: '#/components/responses/ApiError' /api/v1/rules/{rule_set_id}/link: parameters: - name: rule_set_id in: path required: true schema: type: string format: uuid post: tags: - Rules summary: Привязать набор правил к категории operationId: rulesLinkToCategory security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CategoryRuleLinkUpsertRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CategoryRuleLinkUpsertResponse' '404': $ref: '#/components/responses/ApiError' /api/v1/rules/{rule_set_id}/unlink/{category_id}: parameters: - name: rule_set_id in: path required: true schema: type: string format: uuid - name: category_id in: path required: true schema: type: string format: uuid delete: tags: - Rules summary: Отвязать набор правил от категории operationId: rulesUnlinkFromCategory security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CategoryRuleLinkDeleteResponse' '404': $ref: '#/components/responses/ApiError' /api/v1/settings: get: tags: - Settings summary: Получить глобальные настройки (admin/system) operationId: settingsGet responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SettingsGetResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' patch: tags: - Settings summary: Обновить глобальные настройки (admin/system) operationId: settingsPatch requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SettingsUpdateRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SettingsUpdateResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' post: tags: - Settings summary: Alias на PATCH (для интеграции) operationId: settingsPost requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SettingsUpdateRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SettingsUpdateResponse' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/cashback/offers: get: tags: - Cashback summary: Персонализированные предложения кэшбэка operationId: cashbackOffers description: | Возвращает список категорий кэшбэка, персонализированных для текущего пользователя. **Pipeline:** 1. Загрузка профиля пользователя → `DslContext` (age, city, country, sex, role, registration_days) 2. Получение всех ACTIVE категорий + привязанных DSL-правил 3. Фильтрация через DSL engine — правила таргетинга (`AGE >= 18 AND CITY IN ("Москва")`) 4. ML-ранжирование через `POST /ml/score` (3-retry, 150/300/450ms backoff, math fallback) 5. Сортировка по `rate_permille` DESC **DSL-фильтрация:** - Категория без привязанных правил → доступна всем - Правило с `is_default=true` → всегда проходит - Пустой или `"true"` DSL → всегда проходит - Иначе: парсинг + evaluation DSL-выражения против профиля **ML scoring:** - Успех: `source = "ml"`, ставка из `rate_permille`, объяснение из `explanation` - Fallback: `source = "math_fallback"`, ставка = budget_weight × 60% + capacity × 40% security: - bearerAuth: [] responses: '200': description: Персонализированные предложения content: application/json: schema: $ref: '#/components/schemas/CashbackOffersResponse' '401': $ref: '#/components/responses/Unauthorized' '404': description: Пользователь не найден content: application/json: schema: $ref: '#/components/schemas/ApiError' '429': $ref: '#/components/responses/TooManyRequests' /api/v1/cashback/select: post: tags: - Cashback summary: Выбрать категории кэшбэка на период operationId: cashbackSelect security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CashbackSelectRequest' responses: '200': description: Выбор подтверждён content: application/json: schema: $ref: '#/components/schemas/CashbackSelectionResponse' '409': description: Активный выбор ещё не истёк content: application/json: schema: $ref: '#/components/schemas/ApiError' '422': description: Кол-во категорий > categories_user_can_choose content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/cashback/current: get: tags: - Cashback summary: Текущий активный выбор кэшбэка operationId: cashbackCurrent security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CashbackCurrentResponse' '404': description: Нет активного выбора content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/cashback/stats: get: tags: - Cashback summary: Полная статистика кэшбэка operationId: cashbackStats security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CashbackStatsResponse' /api/v1/cashback/stats/summary: get: tags: - Cashback summary: Краткая сводка кэшбэка operationId: cashbackStatsSummary security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CashbackSummaryResponse' /api/v1/cashback/history: get: tags: - Cashback summary: История транзакций кэшбэка operationId: cashbackHistory security: - bearerAuth: [] parameters: - name: limit in: query schema: type: integer default: 50 minimum: 1 maximum: 200 - name: offset in: query schema: type: integer default: 0 minimum: 0 - name: category_id in: query schema: type: string format: uuid description: Фильтр по категории responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/CashbackHistoryResponse' /api/v1/progress: get: tags: - Progress summary: Обзор прогресса пользователя description: Выбранные категории, колесо фортуны, сводка трат operationId: progressOverview security: - bearerAuth: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/ProgressOverviewResponse' /api/v1/progress/select: post: tags: - Progress summary: Выбрать/подтвердить категории кэшбэка operationId: progressSelect security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SelectCategoriesRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SelectCategoriesResponse' '400': $ref: '#/components/responses/ApiError' /api/v1/progress/fortune-wheel: post: tags: - Progress summary: Сохранить результат колеса фортуны operationId: progressFortuneWheel security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/FortuneWheelRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/FortuneWheelResponse' '400': $ref: '#/components/responses/ApiError' '409': $ref: '#/components/responses/ApiError' /api/v1/progress/spending: get: tags: - Progress summary: Детальная история трат кэшбэка operationId: progressSpending security: - bearerAuth: [] parameters: - name: limit in: query schema: type: integer default: 50 - name: offset in: query schema: type: integer default: 0 responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/SpendingResponse' /api/v1/metrics/prometheus: get: tags: - Metrics summary: Prometheus метрики operationId: metricsPrometheus description: | Экспорт метрик в формате Prometheus text exposition. Требуется авторизация: admin или system_permission. security: - bearerAuth: [] responses: '200': description: Метрики в text/plain формате content: text/plain: schema: type: string '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/metrics/ui: get: tags: - Metrics summary: HTML дашборд метрик operationId: metricsUi description: | Интерактивный HTML-дашборд с визуализацией метрик. Auto-refresh каждые 10 секунд. Требуется авторизация: admin или system_permission. security: - bearerAuth: [] responses: '200': description: HTML страница content: text/html: schema: type: string '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/ml/health: get: tags: - ML summary: Health-check ML-сервиса operationId: mlHealthCheck description: | **Internal.** Проксируется backend → `ml:8005/health`. Проверка доступности ML и состояния моделей. security: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MlHealthResponse' /api/v1/ml/status: get: tags: - ML summary: Статус ML-сервиса operationId: mlStatus description: | Проксируется backend → `ml:8005/health`. Возвращает обёртку: `available` (bool) + `base_url` + детали health. security: - bearerAuth: [] responses: '200': description: Статус ML content: application/json: schema: $ref: '#/components/schemas/MlStatusResponse' '401': $ref: '#/components/responses/Unauthorized' /api/v1/ml/score: post: tags: - ML summary: Персонализация — ранжирование категорий operationId: mlScore description: | **Internal.** Проксируется backend → `ml:8005/ml/score`. Backend вызывает ML, чтобы получить ранжирование категорий, ставку и expected_cost. ⚠ **Конвертация единиц:** - ML принимает и возвращает суммы в **копейках** (`MoneyKopeks`). - Backend конвертирует в **рубли**: `floor(kopeks / 100)`. - Остаток после деления отбрасывается (truncate, не round). ML рекомендует, но backend всегда валидирует: - ставку в диапазоне категории - бюджеты (категория / общий) security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MlScoreRequest' responses: '200': description: Рекомендации ML content: application/json: schema: $ref: '#/components/schemas/MlScoreResponse' '400': description: Некорректный запрос content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/ml/profit: post: tags: - ML summary: Оценка прибыли банка operationId: mlProfit description: | **Internal.** Проксируется backend → `ml:8005/ml/profit`. profit = predicted_GMV × interchange_rate − min(cashback, budget). security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MlProfitRequest' responses: '200': description: Прибыль content: application/json: schema: $ref: '#/components/schemas/MlProfitResponse' '400': description: Некорректный запрос content: application/json: schema: $ref: '#/components/schemas/ApiError' '401': $ref: '#/components/responses/Unauthorized' /api/v1/ml/notify: post: tags: - ML summary: Уведомление ML о новых данных operationId: mlNotify description: | **Internal.** Проксируется backend → `ml:8005/ml/notify`. Backend уведомляет ML о событиях: - `categories_changed` — изменились категории - `budget_changed` — изменился бюджет - `rules_activated` — активированы правила - `transactions_ingested` — загружены транзакции - `accruals_updated` — обновлены начисления ⚠ Суммы в `subject` передаются в **копейках** (ML-формат). security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MlNotifyEvent' responses: '200': description: Принято content: application/json: schema: $ref: '#/components/schemas/MlNotifyResponse' '400': description: Некорректный запрос content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/ml/metrics: get: tags: - ML summary: Метрики ML-сервиса operationId: mlMetricsGet description: | **Internal.** Проксируется backend → `ml:8005/metrics`. Метрики качества модели, латенси и нагрузки. security: [] responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MlMetricsResponse' /api/v1/ml/drift: get: tags: - ML summary: Drift-мониторинг ML operationId: mlDrift description: | **Admin/System.** Проксируется backend → `ml:8005/drift`. Сравнивает распределения фичей с эталонными. security: - bearerAuth: [] responses: '200': description: Drift-отчёт content: application/json: schema: $ref: '#/components/schemas/MlDriftResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/ml/reload: post: tags: - ML summary: Перезагрузка моделей ML operationId: mlReload description: | **Admin/System.** Проксируется backend → `ml:8005/reload`. Перезагружает модели и feature store без рестарта. security: - bearerAuth: [] responses: '200': description: Модели перезагружены content: application/json: schema: $ref: '#/components/schemas/MlReloadResponse' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/v1/debug/health: get: tags: - Debug summary: DEV-only health-check operationId: debugHealth security: [] responses: '200': description: OK content: application/json: schema: type: object required: - ok - env properties: ok: type: boolean env: type: string /api/v1/debug/db/info: get: tags: - Debug summary: DEV-only информация о БД operationId: debugDbInfo security: [] responses: '200': description: OK content: application/json: schema: type: object required: - db - user - version properties: db: type: string user: type: string version: type: string /api/v1/debug/db/tables: get: tags: - Debug summary: DEV-only список таблиц operationId: debugDbTables security: [] responses: '200': description: OK content: application/json: schema: type: object required: - items properties: items: type: array items: type: object required: - schema - name properties: schema: type: string name: type: string /api/v1/debug/db/users: get: tags: - Debug summary: DEV-only список пользователей operationId: debugDbUsersList security: [] parameters: - name: limit in: query schema: type: integer default: 50 - name: offset in: query schema: type: integer default: 0 - name: search in: query schema: type: string responses: '200': description: OK content: application/json: schema: type: object required: - limit - offset - items properties: limit: type: integer offset: type: integer items: type: array items: $ref: '#/components/schemas/DebugUserItem' /api/v1/debug/db/users/{user_id}: parameters: - name: user_id in: path required: true schema: type: string format: uuid get: tags: - Debug summary: DEV-only получить пользователя по ID operationId: debugDbUsersGet security: [] responses: '200': description: OK content: application/json: schema: type: object required: - user properties: user: $ref: '#/components/schemas/DebugUserDetail' /api/v1/debug/db/wipe: post: tags: - Debug summary: DEV-only очистка данных БД operationId: debugDbWipe requestBody: required: true content: application/json: schema: type: object required: - confirm properties: confirm: type: boolean responses: '200': description: OK content: application/json: schema: type: object required: - ok - kept_user_id - deleted properties: ok: type: boolean kept_user_id: type: string format: uuid deleted: type: object properties: revoked_tokens: type: integer admin_requests: type: integer user_profiles: type: integer users: type: integer '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/ApiError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/db/counts: post: tags: - Debug summary: DEV-only сводка по количеству записей operationId: debugDbCounts requestBody: required: true content: application/json: schema: type: object required: - confirm properties: confirm: type: boolean responses: '200': description: OK content: application/json: schema: type: object required: - ok - actor_user_id - counts properties: ok: type: boolean actor_user_id: type: string format: uuid counts: type: object properties: users: type: integer user_profiles: type: integer admin_requests: type: integer revoked_tokens: type: integer '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/ApiError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/auth/mint-token: post: tags: - Debug summary: DEV-only выпуск токена для произвольного user_id operationId: debugAuthMintToken requestBody: required: true content: application/json: schema: type: object required: - user_id properties: user_id: type: string format: uuid responses: '200': description: OK content: application/json: schema: type: object required: - ok - minted_by - access_token - user properties: ok: type: boolean minted_by: type: string format: uuid access_token: type: string user: type: object required: - id - login - role - system_permission properties: id: type: string format: uuid login: type: string role: type: string system_permission: type: boolean '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/bootstrap/system-admin: post: tags: - Debug summary: DEV-only создание system-admin аккаунта operationId: debugBootstrapSystemAdmin security: [] requestBody: required: true content: application/json: schema: type: object required: - login - password properties: login: type: string password: type: string minLength: 8 responses: '200': description: OK content: application/json: schema: type: object required: - access_token - user properties: access_token: type: string user: type: object required: - id - login - role - status - system_permission properties: id: type: string format: uuid login: type: string role: type: string status: type: string system_permission: type: boolean '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/ApiError' '409': description: Conflict content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/fake/users: post: tags: - Debug summary: DEV-only генерация тестовых пользователей operationId: debugFakeUsersGenerate requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DebugFakeUsersRequest' responses: '200': description: OK content: application/json: schema: type: object required: - ok - count - items properties: ok: type: boolean count: type: integer items: type: array items: $ref: '#/components/schemas/DebugUserItem' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/cache/info: get: operationId: debugCacheInfo summary: Cache server info description: Returns INFO output from the cache server (Valkey/Redis). DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Cache info content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/cache/dbsize: get: operationId: debugCacheDbsize summary: Cache key count description: Returns the number of keys in the cache database. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Key count content: application/json: schema: type: object properties: ok: type: boolean dbsize: type: integer '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/cache/flush: post: operationId: debugCacheFlush summary: Flush all cache keys description: Deletes all keys from the cache database. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Flushed content: application/json: schema: type: object properties: ok: type: boolean '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/cache/get/{key}: get: operationId: debugCacheGet summary: Get cache value by key description: Returns the cached value for the given key. DEV-only. tags: - Debug security: - bearerAuth: [] parameters: - name: key in: path required: true schema: type: string responses: '200': description: Cache value content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/cache/del/{key}: delete: operationId: debugCacheDel summary: Delete cache key description: Deletes a specific key from the cache. DEV-only. tags: - Debug security: - bearerAuth: [] parameters: - name: key in: path required: true schema: type: string responses: '200': description: Deleted content: application/json: schema: type: object properties: ok: type: boolean deleted: type: integer '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/db/categories: get: operationId: debugDbCategories summary: Dump all categories description: Returns all rows from the categories table. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Categories dump content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/db/rules: get: operationId: debugDbRules summary: Dump all rule sets description: Returns all rows from the rule_sets and category_rule_sets tables. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Rules dump content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/db/settings: get: operationId: debugDbSettings summary: Dump global settings description: Returns all rows from the global_settings table. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Settings dump content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/db/selections: get: operationId: debugDbSelections summary: Dump all cashback selections description: Returns all rows from the cashback_selections table. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Selections dump content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/db/transactions: get: operationId: debugDbTransactions summary: Dump all transactions description: Returns all rows from the transactions table. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Transactions dump content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/db/fortune-wheel: get: operationId: debugDbFortuneWheel summary: Dump fortune wheel data description: Returns all rows from the fortune_wheel table. DEV-only. tags: - Debug security: - bearerAuth: [] responses: '200': description: Fortune wheel dump content: application/json: schema: type: object '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' /api/v1/debug/stress/ping: post: operationId: debugStressPing summary: Stress-test ping description: | Lightweight no-auth endpoint for stress testing. Returns `{ "ok": true, "latency_us": }`. DEV-only. tags: - Debug security: [] responses: '200': description: Pong with latency content: application/json: schema: type: object properties: ok: type: boolean latency_us: type: integer description: Handler latency in microseconds '403': description: Not in dev mode content: application/json: schema: $ref: '#/components/schemas/ApiError' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT responses: BadRequest: description: Bad request content: application/json: schema: $ref: '#/components/schemas/ApiError' Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ApiError' Forbidden: description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ApiError' NotFound: description: Not found content: application/json: schema: $ref: '#/components/schemas/ApiError' TooManyRequests: description: Too many requests — rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/ApiError' ApiError: type: object required: - code - message properties: code: type: string example: INVALID_REQUEST message: type: string example: Something went wrong schemas: ApiError: type: object required: - code - message properties: code: type: string example: INVALID_REQUEST message: type: string example: Something went wrong OkResponse: type: object required: - ok properties: ok: type: boolean const: true HealthResponse: type: object required: - ok - db_ok - uptime_ms - db_check_ms - service - version - env - pid properties: ok: type: boolean description: Общий статус (true если db_ok) db_ok: type: boolean description: Результат SELECT 1 к PostgreSQL uptime_ms: type: integer description: Время работы процесса в миллисекундах db_check_ms: type: integer description: Время выполнения DB health-check в мс service: type: string example: backend version: type: string example: 0.2.0 env: type: string example: dev pid: type: integer description: PID процесса now_unix_ms: type: - integer - 'null' description: Текущее время (Unix timestamp в мс) MetaResponse: type: object required: - service - version - endpoints - observability - generated_at properties: service: type: string example: t-cat-cashback-backend version: type: string example: 0.2.0 env: type: - string - 'null' description: Переменная APP_ENV (может отсутствовать) endpoints: $ref: '#/components/schemas/MetaEndpoints' observability: $ref: '#/components/schemas/MetaObservability' generated_at: type: string description: Время генерации ответа MetaEndpoints: type: object required: - health - metrics_prometheus - meta - api_v1_root - openapi_yaml - openapi_json - swagger_ui - redoc_ui properties: health: type: string example: /health metrics_prometheus: type: string example: /metrics meta: type: string example: /api/meta api_v1_root: type: string example: /api/v1 openapi_yaml: type: string example: /docs/openapi/openapi.yaml openapi_json: type: string example: /docs/openapi/openapi.json swagger_ui: type: string example: /docs/openapi/swagger-ui.html redoc_ui: type: string example: /docs/openapi/redoc-ui.html MetaObservability: type: object required: - prometheus_enabled - gathered_metrics_families_count properties: prometheus_enabled: type: boolean gathered_metrics_families_count: type: integer AuthLoginRequest: type: object description: Запрос на вход в систему required: - login - password properties: login: type: string description: Логин пользователя example: ivan_petrov password: type: string minLength: 8 description: Пароль (минимум 8 символов) example: securePass123 AuthRegisterRequest: type: object description: Запрос на регистрацию нового пользователя required: - login - password - name - role properties: login: type: string description: Логин для нового аккаунта example: ivan_petrov password: type: string minLength: 8 description: Пароль (минимум 8 символов) example: securePass123 name: type: string description: Отображаемое имя пользователя example: Иван Петров role: type: string enum: - user - admin description: 'Роль: user — сразу активен, admin — требует одобрения' example: user AuthSessionResponse: type: object description: Ответ с токеном и данными пользователя required: - access_token - user properties: access_token: type: string description: JWT access-токен для авторизации example: eyJhbGciOiJIUzI1NiIs... user: $ref: '#/components/schemas/MeDto' LogoutResponse: type: object description: Ответ на выход из системы required: - ok properties: ok: type: boolean description: Успешность операции example: true RegisterCreatedResponse: type: object description: Пользователь зарегистрирован и сразу активен (роль user) required: - status - user_id properties: status: type: string enum: - active description: Статус аккаунта после регистрации user_id: type: string format: uuid description: UUID созданного пользователя RegisterPendingApprovalResponse: type: object description: Пользователь зарегистрирован, ожидает одобрения админа (роль admin) required: - status - user_id - admin_request properties: status: type: string enum: - pending_approval description: Статус аккаунта — ожидает одобрения user_id: type: string format: uuid description: UUID созданного пользователя admin_request: $ref: '#/components/schemas/AuthAdminRequestDto' AuthAdminRequestDto: type: object description: Заявка на получение роли администратора required: - id - status - created_at - updated_at properties: id: type: string format: uuid description: UUID заявки status: type: string enum: - APPROVED - PENDING_APPROVAL - REJECTED description: Текущий статус заявки comment: type: - string - 'null' description: Комментарий при одобрении/отклонении created_at: type: string format: date-time description: Дата создания заявки updated_at: type: string format: date-time description: Дата последнего обновления заявки reviewed_by_admin_id: type: - string - 'null' format: uuid description: UUID администратора, рассмотревшего заявку reviewed_at: type: - string - 'null' format: date-time description: Дата рассмотрения заявки UserRole: type: string enum: - USER - ADMIN UserStatus: type: string enum: - ACTIVE - BLOCKED - DISABLED Sex: type: string enum: - MALE - FEMALE - AMIR UserTimestamps: type: object required: - created_at - updated_at properties: created_at: type: string format: date-time updated_at: type: string format: date-time blocked_at: type: - string - 'null' format: date-time UserProfile: type: object properties: birthday: type: - string - 'null' format: date sex: oneOf: - $ref: '#/components/schemas/Sex' - type: 'null' email: type: - string - 'null' phone_number: type: - string - 'null' city: type: - string - 'null' description: Город пользователя example: Москва country: type: - string - 'null' description: Страна пользователя example: Россия MeDto: type: object required: - id - name - login - role - status - system_permission - timestamps - profile - to_be_deleted properties: id: type: string format: uuid name: type: string login: type: string role: $ref: '#/components/schemas/UserRole' status: $ref: '#/components/schemas/UserStatus' system_permission: type: boolean timestamps: $ref: '#/components/schemas/UserTimestamps' profile: $ref: '#/components/schemas/UserProfile' admin_request: oneOf: - $ref: '#/components/schemas/AdminRequest' - type: 'null' to_be_deleted: type: boolean UserDto: allOf: - $ref: '#/components/schemas/MeDto' UserGetResponse: type: object required: - user properties: user: $ref: '#/components/schemas/UserDto' UsersListResponse: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/UserDto' MeGetResponse: type: object required: - user properties: user: $ref: '#/components/schemas/MeDto' MeUpdateRequest: type: object additionalProperties: false properties: login: type: string name: type: string profile: type: object properties: birthday: type: string format: date sex: $ref: '#/components/schemas/Sex' email: type: string phone_number: type: string city: type: string country: type: string to_be_deleted: type: boolean MeUpdateResponse: type: object required: - user properties: user: $ref: '#/components/schemas/MeDto' UserUpsertRequest: type: object required: - login - password - name properties: login: type: string password: type: string minLength: 8 name: type: string profile: type: object properties: birthday: type: string format: date sex: $ref: '#/components/schemas/Sex' email: type: string phone_number: type: string city: type: string country: type: string UserPatchRequest: type: object additionalProperties: false properties: login: type: string name: type: string password: type: string minLength: 8 role: $ref: '#/components/schemas/UserRole' status: $ref: '#/components/schemas/UserStatus' system_permission: type: boolean to_be_deleted: type: boolean profile: type: object properties: birthday: type: string format: date sex: $ref: '#/components/schemas/Sex' email: type: string phone_number: type: string city: type: string country: type: string AdminListItem: type: object required: - id - name - login - system_permission - status - created_at - updated_at properties: id: type: string format: uuid name: type: string login: type: string system_permission: type: boolean status: type: string created_at: type: string updated_at: type: string AdminCreateRequest: type: object required: - login - password - name properties: login: type: string password: type: string minLength: 8 name: type: string system_permission: type: boolean default: false AdminCreateResponse: type: object required: - admin_id properties: admin_id: type: string format: uuid AdminStatsResponse: type: object required: - users - categories - transactions - budget - generated_at properties: users: $ref: '#/components/schemas/AdminUserStatsResponse' categories: type: object required: - total - active - draft - disabled properties: total: type: integer active: type: integer draft: type: integer disabled: type: integer transactions: type: object required: - total_count - total_cashback_rub - current_month_rub properties: total_count: type: integer total_cashback_rub: type: integer current_month_rub: type: integer budget: $ref: '#/components/schemas/AdminBudgetStatsResponse' generated_at: type: string format: date-time AdminBudgetStatsResponse: type: object required: - global_budget_rub - total_allocated_rub - total_spent_rub - total_reserved_rub - utilization_percent properties: global_budget_rub: type: integer total_allocated_rub: type: integer description: Сумма budget_limit_rub всех категорий total_spent_rub: type: integer description: Сумма budget_spent_rub всех категорий total_reserved_rub: type: integer description: Сумма budget_reserved_rub всех категорий utilization_percent: type: number format: double description: (spent + reserved) / allocated * 100 AdminUserStatsResponse: type: object required: - total - admins - users_active - users_blocked - pending_approval_requests properties: total: type: integer admins: type: integer users_active: type: integer users_blocked: type: integer pending_approval_requests: type: integer ReviewedByAdmin: type: object required: - id properties: id: type: string format: uuid name: type: - string - 'null' AdminRequestStatus: type: string enum: - APPROVED - PENDING_APPROVAL - REJECTED AdminRequestTimestamps: type: object required: - created_at - updated_at properties: created_at: type: string format: date-time updated_at: type: string format: date-time reviewed_at: type: - string - 'null' format: date-time AdminRequest: type: object required: - id - status - timestamps properties: id: type: string format: uuid status: $ref: '#/components/schemas/AdminRequestStatus' comment: type: - string - 'null' reviewed_by_admin: oneOf: - $ref: '#/components/schemas/ReviewedByAdmin' - type: 'null' timestamps: $ref: '#/components/schemas/AdminRequestTimestamps' AdminRequestsActorDto: type: object required: - current_role - system_permission - can_review_admin_requests properties: current_role: $ref: '#/components/schemas/UserRole' system_permission: type: boolean can_review_admin_requests: type: boolean AdminRequestStatusDto: type: string enum: - APPROVED - PENDING_APPROVAL - REJECTED AdminRequestTimestampsDto: type: object required: - created_at - updated_at properties: created_at: type: string format: date-time updated_at: type: string format: date-time reviewed_at: type: - string - 'null' format: date-time AdminRequestDto: type: object required: - id - user_id - status - timestamps properties: id: type: string format: uuid user_id: type: string format: uuid user_login: type: - string - 'null' user_name: type: - string - 'null' status: $ref: '#/components/schemas/AdminRequestStatusDto' comment: type: - string - 'null' reviewed_by_admin: oneOf: - $ref: '#/components/schemas/ReviewedByAdmin' - type: 'null' timestamps: $ref: '#/components/schemas/AdminRequestTimestampsDto' AdminRequestReviewRequest: type: object additionalProperties: false properties: comment: type: - string - 'null' AdminRequestsListResponse: type: object required: - actor - items properties: actor: $ref: '#/components/schemas/AdminRequestsActorDto' items: type: array items: $ref: '#/components/schemas/AdminRequestDto' AdminRequestGetResponse: type: object required: - actor - admin_request properties: actor: $ref: '#/components/schemas/AdminRequestsActorDto' admin_request: $ref: '#/components/schemas/AdminRequestDto' AdminRequestApproveResponse: type: object required: - actor - admin_request properties: actor: $ref: '#/components/schemas/AdminRequestsActorDto' admin_request: $ref: '#/components/schemas/AdminRequestDto' promoted_user_id: type: - string - 'null' format: uuid AdminRequestRejectResponse: type: object required: - actor - admin_request properties: actor: $ref: '#/components/schemas/AdminRequestsActorDto' admin_request: $ref: '#/components/schemas/AdminRequestDto' AdminRequestsStatsResponse: type: object required: - pending - approved - rejected - total properties: pending: type: integer approved: type: integer rejected: type: integer total: type: integer BatchOperationRequest: type: object required: - request_ids properties: request_ids: type: array items: type: string format: uuid comment: type: string BatchOperationResponse: type: object required: - processed - skipped - errors properties: processed: type: integer skipped: type: integer errors: type: array items: type: string CategoryStatusDto: type: string description: Статус категории кэшбэка enum: - DRAFT - ACTIVE - DISABLED CategoryTimestampsDto: type: object description: Временные метки создания и обновления категории required: - created_at - updated_at properties: created_at: type: string format: date-time description: Дата создания категории example: '2026-01-15T10:30:00Z' updated_at: type: string format: date-time description: Дата последнего обновления example: '2026-03-01T14:00:00Z' CategoryBudgetDto: type: object description: Бюджет категории кэшбэка required: - limit_rub - spent_rub - reserved_rub properties: limit_rub: type: integer minimum: 0 description: Лимит бюджета категории в рублях example: 50000 spent_rub: type: integer minimum: 0 description: Потрачено из бюджета в рублях example: 12300 reserved_rub: type: integer minimum: 0 description: Зарезервировано из бюджета в рублях example: 5000 CategoryRuleDto: type: object description: Правило таргетинга, привязанное к категории required: - id - name - description - enabled - is_default properties: id: type: string format: uuid description: UUID правила name: type: string description: Название правила example: Молодые пользователи description: type: string description: Описание правила example: Пользователи от 18 до 30 лет enabled: type: boolean description: Активно ли правило example: true created_by_admin: oneOf: - $ref: '#/components/schemas/ReviewedByAdmin' - type: 'null' is_default: type: boolean description: Правило по умолчанию (применяется ко всем) example: false CategoryDto: type: object description: Полное представление категории кэшбэка required: - id - name - subname - description - short_description - icon_ref - status - timestamps - budget - rules properties: id: type: string format: uuid description: UUID категории name: type: string description: Название категории кэшбэка example: Рестораны subname: type: string description: Подзаголовок категории example: Еда и напитки description: type: string description: Полное описание категории example: Кэшбэк за оплату в ресторанах и кафе short_description: type: string description: Краткое описание для карточки example: Рестораны и кафе icon_ref: type: string description: Ссылка на иконку категории example: restaurant status: $ref: '#/components/schemas/CategoryStatusDto' timestamps: $ref: '#/components/schemas/CategoryTimestampsDto' budget: $ref: '#/components/schemas/CategoryBudgetDto' rules: type: array description: Список правил таргетинга, привязанных к категории items: $ref: '#/components/schemas/CategoryRuleDto' CategoryGetResponse: type: object required: - category properties: category: $ref: '#/components/schemas/CategoryDto' CategoriesListResponse: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/CategoryDto' CategoryUpsertRequest: type: object description: Запрос на создание или обновление категории кэшбэка required: - name - subname - description - short_description - icon_ref - status - budget_limit_rub properties: name: type: string description: Название категории кэшбэка example: Рестораны subname: type: string description: Подзаголовок категории example: Еда и напитки description: type: string description: Полное описание категории example: Кэшбэк за оплату в ресторанах и кафе short_description: type: string description: Краткое описание для карточки example: Рестораны и кафе icon_ref: type: string description: Ссылка на иконку категории example: restaurant status: $ref: '#/components/schemas/CategoryStatusDto' budget_limit_rub: type: integer minimum: 0 description: Лимит бюджета категории в рублях example: 50000 CategoryUpsertResponse: type: object required: - category properties: category: $ref: '#/components/schemas/CategoryDto' RuleSetDto: type: object description: Полное представление набора правил таргетинга required: - id - name - description - dsl - created_by_admin - timestamps properties: id: type: string format: uuid description: UUID набора правил name: type: string description: Название набора правил example: Молодые пользователи description: type: string description: Описание набора правил example: Таргетинг на пользователей от 18 до 30 лет dsl: type: string description: DSL-выражение для таргетинга пользователей example: AGE >= 18 AND AGE <= 30 created_by_admin: $ref: '#/components/schemas/rules_ReviewedByAdmin' timestamps: $ref: '#/components/schemas/RuleSetTimestampsDto' RuleSetGetResponse: type: object required: - rule_set properties: rule_set: $ref: '#/components/schemas/RuleSetDto' RuleSetsListResponse: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/RuleSetDto' RuleSetUpsertRequest: type: object description: Запрос на создание или обновление набора правил required: - name - description - dsl properties: name: type: string minLength: 1 description: Название набора правил example: Молодые пользователи description: type: string description: Описание набора правил example: Таргетинг на пользователей от 18 до 30 лет dsl: type: string minLength: 1 description: DSL-выражение для таргетинга пользователей example: AGE >= 18 AND AGE <= 30 RuleSetUpsertResponse: type: object required: - rule_set properties: rule_set: $ref: '#/components/schemas/RuleSetDto' RuleSetTimestampsDto: type: object description: Временные метки создания и обновления набора правил required: - created_at - updated_at properties: created_at: type: string format: date-time description: Дата создания набора правил example: '2026-01-10T09:00:00Z' updated_at: type: string format: date-time description: Дата последнего обновления example: '2026-02-20T16:30:00Z' CategoryRuleLinkUpsertRequest: type: object description: Запрос на привязку правила к категории required: - rule_set_id - enabled - is_default properties: rule_set_id: type: string format: uuid description: UUID набора правил для привязки enabled: type: boolean description: Активировать привязку сразу example: true is_default: type: boolean description: Сделать правилом по умолчанию для категории example: false CategoryRuleLinkUpsertResponse: type: object description: Результат привязки правила к категории required: - category_id - rule properties: category_id: type: string format: uuid description: UUID категории rule: $ref: '#/components/schemas/CategoryRuleDto' CategoryRuleLinkDeleteResponse: type: object description: Результат удаления привязки правила от категории required: - ok - category_id - rule_set_id properties: ok: type: boolean description: Успешность операции category_id: type: string format: uuid description: UUID категории rule_set_id: type: string format: uuid description: UUID набора правил CashbackPredictionDto: type: object required: - daily_savings - total_month_rub properties: daily_savings: type: array items: type: integer description: 30 значений — накопительная экономия по дням (руб) total_month_rub: type: integer description: Итого экономия за месяц (руб) CashbackSelectRequest: type: object description: Запрос на выбор категорий кэшбэка required: - category_ids properties: category_ids: type: array items: type: string format: uuid description: UUID категорий, length <= categories_user_can_choose CashbackSelectionResponse: type: object description: Результат выбора категорий кэшбэка с прогнозом required: - selected_at - next_selection_at - categories - prediction properties: selected_at: type: string format: date-time description: Время выбора категорий next_selection_at: type: string format: date-time description: Время следующего доступного выбора categories: type: array description: Выбранные категории items: $ref: '#/components/schemas/SelectedCategoryBriefDto' prediction: $ref: '#/components/schemas/CashbackPredictionDto' CashbackCurrentResponse: type: object description: Текущий выбор кэшбэка пользователя required: - category_ids - categories - selected_at - next_selection_at - prediction - days_remaining properties: category_ids: type: array description: UUID выбранных категорий items: type: string format: uuid categories: type: array description: Выбранные категории с краткой информацией items: $ref: '#/components/schemas/SelectedCategoryBriefDto' selected_at: type: string format: date-time description: Время последнего выбора next_selection_at: type: string format: date-time description: Время следующего доступного выбора prediction: $ref: '#/components/schemas/CashbackPredictionDto' days_remaining: type: integer description: Дней до следующего выбора example: 14 CashbackStatsResponse: type: object description: Детальная статистика кэшбэка пользователя required: - user_id - period - total_cashback_rub - total_transactions - by_category - monthly_trend properties: user_id: type: string format: uuid description: UUID пользователя period: $ref: '#/components/schemas/StatsPeriodDto' total_cashback_rub: type: integer description: Общая сумма кэшбэка за период в рублях example: 12500 total_transactions: type: integer description: Общее количество транзакций за период example: 156 by_category: type: array description: Разбивка кэшбэка по категориям items: $ref: '#/components/schemas/CategoryStatsDto' monthly_trend: type: array description: Помесячный тренд кэшбэка items: $ref: '#/components/schemas/MonthlyTrendDto' CashbackSummaryResponse: type: object description: Краткая сводка по кэшбэку пользователя required: - total_cashback_rub - total_transactions - active_categories - current_month_rub properties: total_cashback_rub: type: integer description: Общий кэшбэк за всё время в рублях example: 25000 total_transactions: type: integer description: Общее количество транзакций example: 320 active_categories: type: integer description: Количество активных категорий кэшбэка example: 3 current_month_rub: type: integer description: Кэшбэк за текущий месяц в рублях example: 4200 CashbackHistoryResponse: type: object description: История транзакций кэшбэка с пагинацией required: - transactions - total properties: transactions: type: array description: Список транзакций items: $ref: '#/components/schemas/TransactionItemDto' total: type: integer description: Общее кол-во транзакций (для пагинации) example: 320 CategoryStatsDto: type: object description: Статистика кэшбэка по одной категории required: - category_id - category_name - icon_ref - cashback_rub - transaction_count - percentage properties: category_id: type: string format: uuid description: UUID категории category_name: type: string description: Название категории example: Рестораны icon_ref: type: string description: Ссылка на иконку example: restaurant cashback_rub: type: integer description: Сумма кэшбэка по категории в рублях example: 1500 transaction_count: type: integer description: Количество транзакций в категории example: 23 percentage: type: number format: double description: Доля категории в общем кэшбэке (%) example: 35.5 MonthlyTrendDto: type: object description: Тренд кэшбэка за месяц required: - month - cashback_rub - transaction_count properties: month: type: string description: Месяц в формате YYYY-MM example: 2026-03 cashback_rub: type: integer description: Сумма кэшбэка за месяц в рублях example: 4200 transaction_count: type: integer description: Количество транзакций за месяц example: 47 TransactionItemDto: type: object description: Транзакция кэшбэка required: - id - category_id - category_name - amount_rub - description - created_at properties: id: type: string format: uuid description: UUID транзакции category_id: type: string format: uuid description: UUID категории category_name: type: string description: Название категории example: Рестораны amount_rub: type: integer description: Сумма кэшбэка в рублях example: 150 description: type: string description: Описание транзакции example: Оплата в Яндекс Еда created_at: type: string format: date-time description: Дата транзакции example: '2026-03-15T12:30:00Z' CashbackOfferDto: type: object description: Персонализированное предложение кэшбэка (после DSL-фильтрации + ML-ранжирования) required: - category_id - name - subname - short_description - icon_ref - budget_remaining_rub - rate_permille - expected_cost_rub - explanation - rank - source properties: category_id: type: string format: uuid description: UUID категории name: type: string description: Название категории example: Рестораны subname: type: string description: Подназвание example: Кафе и рестораны short_description: type: string description: Краткое описание example: 5% кэшбэк на рестораны icon_ref: type: string description: Ссылка на иконку example: restaurant budget_remaining_rub: type: integer description: Остаток бюджета категории в рублях example: 45000 rate_permille: type: integer minimum: 0 maximum: 1000 description: Ставка кэшбэка в промилле (50 = 5.0%) example: 50 expected_cost_rub: type: number description: Ожидаемая стоимость кэшбэка для банка в рублях example: 500 explanation: type: string description: ML-объяснение (SHAP или fallback) example: Высокая активность в категории, оптимальный бюджет rank: type: integer minimum: 1 description: Позиция в ранжировании (1 = лучшее предложение) example: 1 source: type: string description: 'Источник скоринга: ml | math_fallback' example: ml CashbackOffersResponse: type: object description: | Персонализированные предложения кэшбэка для пользователя. Pipeline: 1. Загрузка профиля → DslContext 2. Фильтрация ACTIVE категорий через DSL engine 3. ML-ранжирование через /ml/score (3-retry + math fallback) 4. Сортировка по rate_permille DESC required: - user_id - offers - total_eligible - source properties: user_id: type: string format: uuid description: UUID пользователя offers: type: array description: Отсортированные предложения (rate_permille DESC) items: $ref: '#/components/schemas/CashbackOfferDto' total_eligible: type: integer description: Кол-во категорий, прошедших DSL-фильтрацию example: 8 source: type: string description: 'Источник скоринга: ml | math_fallback | none | dsl_filtered' example: ml GlobalSettingsTimestampsDto: type: object required: - created_at - updated_at properties: created_at: type: string format: date-time updated_at: type: string format: date-time GlobalSettingsDto: type: object required: - categories_to_show - categories_user_can_choose - cashback_budget_rub - timestamps properties: categories_to_show: type: integer minimum: 1 categories_user_can_choose: type: integer minimum: 1 cashback_budget_rub: type: integer minimum: 0 timestamps: $ref: '#/components/schemas/GlobalSettingsTimestampsDto' SettingsGetResponse: type: object required: - settings properties: settings: $ref: '#/components/schemas/GlobalSettingsDto' SettingsUpdateRequest: type: object additionalProperties: false properties: categories_to_show: type: integer minimum: 1 categories_user_can_choose: type: integer minimum: 1 cashback_budget_rub: type: integer minimum: 0 SettingsUpdateResponse: type: object required: - settings properties: settings: $ref: '#/components/schemas/GlobalSettingsDto' SelectedCategoryDto: type: object required: - id - category_id - category_name - is_confirmed - source - created_at properties: id: type: string format: uuid category_id: type: string format: uuid category_name: type: string is_confirmed: type: boolean source: type: string enum: - manual - fortune_wheel created_at: type: string format: date-time FortuneWheelDto: type: object required: - category_id - category_name - period - created_at properties: category_id: type: string format: uuid category_name: type: string period: type: string created_at: type: string format: date-time SpendingSummaryDto: type: object required: - total_spent_rub - categories_count properties: total_spent_rub: type: integer categories_count: type: integer CategorySpendingDto: type: object required: - category_id - category_name - spent_rub properties: category_id: type: string format: uuid category_name: type: string spent_rub: type: integer TransactionDto: type: object required: - id - category_id - category_name - amount_rub - description - created_at properties: id: type: string format: uuid category_id: type: string format: uuid category_name: type: string amount_rub: type: integer description: type: string created_at: type: string format: date-time ProgressOverviewResponse: type: object required: - selections - spending_summary - per_category properties: selections: type: array items: $ref: '#/components/schemas/SelectedCategoryDto' fortune_wheel: $ref: '#/components/schemas/FortuneWheelDto' spending_summary: $ref: '#/components/schemas/SpendingSummaryDto' per_category: type: array items: $ref: '#/components/schemas/CategorySpendingDto' SelectCategoriesRequest: type: object required: - category_ids properties: category_ids: type: array items: type: string format: uuid description: Список ID категорий для выбора SelectCategoriesResponse: type: object required: - selections properties: selections: type: array items: $ref: '#/components/schemas/SelectedCategoryDto' FortuneWheelRequest: type: object required: - category_id properties: category_id: type: string format: uuid description: ID категории из колеса фортуны period: type: string default: current FortuneWheelResponse: type: object required: - fortune_wheel properties: fortune_wheel: $ref: '#/components/schemas/FortuneWheelDto' SpendingResponse: type: object required: - summary - per_category - transactions properties: summary: $ref: '#/components/schemas/SpendingSummaryDto' per_category: type: array items: $ref: '#/components/schemas/CategorySpendingDto' transactions: type: array items: $ref: '#/components/schemas/TransactionDto' MoneyKopeks: type: integer description: | Денежное значение в **копейках** (целое число, без float). Backend конвертирует в рубли: `floor(kopeks / 100)`. Пример: 123450 копеек = 1234 рубля (50 копеек отбрасываются). example: 123450 RatePermille: type: integer minimum: 0 maximum: 1000 description: Ставка в промилле. 10 = 1.0%, 50 = 5.0%. example: 50 RateRange: type: object required: - min_permille - max_permille properties: min_permille: $ref: '#/components/schemas/RatePermille' max_permille: $ref: '#/components/schemas/RatePermille' MlCategoryStatus: type: string enum: - draft - active - paused - archived MlHealthResponse: type: object required: - status - classifier_loaded - regressor_loaded - regressor_reliable - feature_store_size - known_categories - total_requests - total_errors properties: status: type: string example: ok classifier_loaded: type: boolean regressor_loaded: type: boolean regressor_reliable: type: boolean feature_store_size: type: integer known_categories: type: integer total_requests: type: integer total_errors: type: integer MlMetricsResponse: type: object required: - requests - scoring - latency - quality - model properties: requests: type: object required: - total - score - notify properties: total: type: integer score: type: integer notify: type: integer scoring: type: object required: - categories_scored - categories_fallback - fallback_responses - errors properties: categories_scored: type: integer categories_fallback: type: integer fallback_responses: type: integer errors: type: integer latency: type: object required: - avg_ms - total_calls properties: avg_ms: type: number total_calls: type: integer quality: type: object properties: avg_score: type: - number - 'null' score_p25: type: - number - 'null' score_p50: type: - number - 'null' score_p75: type: - number - 'null' zero_predicted_count: type: integer zero_predicted_rate: type: - number - 'null' recent_scores_count: type: integer model: type: object required: - classifier_loaded - regressor_loaded - feature_store_size - known_categories properties: classifier_loaded: type: boolean regressor_loaded: type: boolean feature_store_size: type: integer known_categories: type: integer MlScoreCategoryInput: type: object required: - category_id - status - rate_range - remaining_kopeks - meta properties: category_id: type: string format: uuid status: $ref: '#/components/schemas/MlCategoryStatus' rate_range: $ref: '#/components/schemas/RateRange' remaining_kopeks: allOf: - $ref: '#/components/schemas/MoneyKopeks' description: Остаток бюджета категории в копейках meta: type: object additionalProperties: true MlScoreBudgetsInput: type: object required: - global_remaining_kopeks properties: global_remaining_kopeks: oneOf: - $ref: '#/components/schemas/MoneyKopeks' - type: 'null' description: Остаток общего бюджета в копейках (null = безлимит) MlScoreRequest: type: object required: - request_id - client_id - categories - budgets - features properties: request_id: type: string format: uuid client_id: type: string format: uuid categories: type: array items: $ref: '#/components/schemas/MlScoreCategoryInput' budgets: $ref: '#/components/schemas/MlScoreBudgetsInput' features: type: object additionalProperties: true description: Контекст/фичи для скоринга (безопасный JSON) MlScoreItem: type: object required: - category_id - rate_permille - expected_cost_kopeks - explanation properties: category_id: type: string format: uuid rate_permille: $ref: '#/components/schemas/RatePermille' expected_cost_kopeks: allOf: - $ref: '#/components/schemas/MoneyKopeks' description: | Ожидаемая стоимость в копейках. Backend конвертирует: `expected_cost_rub = floor(expected_cost_kopeks / 100)` explanation: type: string minLength: 1 debug: oneOf: - type: object additionalProperties: true - type: 'null' MlScoreResponse: type: object required: - request_id - items properties: request_id: type: string format: uuid items: type: array items: $ref: '#/components/schemas/MlScoreItem' global_explanation: oneOf: - type: string - type: 'null' MlNotifyEvent: type: object required: - event_id - event_type - occurred_at - subject properties: event_id: type: string format: uuid event_type: type: string enum: - categories_changed - budget_changed - rules_activated - transactions_ingested - accruals_updated occurred_at: type: string format: date-time subject: type: object additionalProperties: true description: JSON контекст события (суммы в копейках) MlNotifyResponse: type: object required: - event_id - accepted properties: event_id: type: string format: uuid accepted: type: boolean message: oneOf: - type: string - type: 'null' MlProfitRequest: type: object description: | Запрос на оценку прибыли банка по набору категорий клиента. profit = predicted_GMV × interchange_rate − min(cashback, budget). required: - request_id - client_id - categories - budgets - interchange_rate properties: request_id: type: string format: uuid client_id: type: string format: uuid categories: type: array items: $ref: '#/components/schemas/MlScoreCategoryInput' budgets: $ref: '#/components/schemas/MlScoreBudgetsInput' interchange_rate: type: number description: Ставка интерчейнджа банка (например 0.015 = 1.5%) example: 0.015 features: type: object additionalProperties: true description: Дополнительные фичи (опционально) MlProfitResponse: type: object required: - request_id - profit_rub properties: request_id: type: string format: uuid profit_rub: type: number description: Прибыль банка в рублях (interchange × GMV − min(cashback, budget)) example: 42.5 MlDriftResponse: type: object description: Отчёт о drift входных данных — сравнение текущих распределений с эталонными. required: - status properties: status: type: string enum: - ok - warning - critical - no_reference - insufficient_data description: Общий статус drift-мониторинга window_size: type: integer description: Размер окна наблюдения (кол-во запросов) critical_features: type: integer description: Кол-во фичей в критическом состоянии warning_features: type: integer description: Кол-во фичей с предупреждением ok_features: type: integer description: Кол-во фичей в норме message: type: - string - 'null' description: Дополнительное сообщение features: type: object additionalProperties: type: object properties: mean_shift_stds: type: number std_ratio: type: number status: type: string ref_mean: type: number ref_std: type: number current_mean: type: number current_std: type: number MlReloadResponse: type: object description: Результат перезагрузки моделей ML-сервиса. required: - status - classifier_loaded - regressor_loaded - feature_store_size properties: status: type: string example: reloaded classifier_loaded: type: boolean regressor_loaded: type: boolean feature_store_size: type: integer MlStatusResponse: type: object description: Статус ML-сервиса (обёртка над /health для backend). required: - available - base_url properties: available: type: boolean description: Доступен ли ML-сервис base_url: type: string description: Базовый URL ML-сервиса example: http://89.169.184.138:8005 detail: oneOf: - $ref: '#/components/schemas/MlHealthResponse' - type: 'null' DebugUserItem: type: object required: - id - login - name - role - status - system_permission properties: id: type: string format: uuid login: type: string name: type: string role: type: string status: type: string system_permission: type: boolean DebugUserDetail: type: object required: - id - login - name - role - status - system_permission properties: id: type: string format: uuid login: type: string name: type: string role: type: string status: type: string system_permission: type: boolean created_at: type: string format: date-time updated_at: type: string format: date-time profile: type: object properties: birthday: type: - string - 'null' format: date sex: type: - string - 'null' email: type: - string - 'null' phone_number: type: - string - 'null' DebugFakeUsersRequest: type: object properties: count: type: integer minimum: 1 maximum: 5000 default: 10 login_prefix: type: string default: fake_user name_prefix: type: string default: Fake User password: type: string minLength: 8 default: Password1 as_admin: type: boolean default: false system_permission: type: boolean default: false rules_ReviewedByAdmin: type: object description: Администратор, создавший или проверивший ресурс required: - id properties: id: type: string format: uuid description: UUID администратора name: type: string nullable: true description: Имя администратора example: Иван Петров SelectedCategoryBriefDto: type: object description: Краткое представление выбранной категории required: - id - name - icon_ref properties: id: type: string format: uuid description: UUID категории name: type: string description: Название категории example: Рестораны icon_ref: type: string description: Ссылка на иконку example: restaurant StatsPeriodDto: type: object description: Период для выборки статистики required: - from - to properties: from: type: string format: date-time description: Начало периода example: '2026-01-01T00:00:00Z' to: type: string format: date-time description: Конец периода example: '2026-03-31T23:59:59Z'