{"openapi":"3.1.0","info":{"title":"LawForecast API Gateway","description":"Публичный API платформы LawForecast. Базовый путь: `/api/v1`.\n\n## Аутентификация\n\nКаждый запрос, кроме `/auth/*` и `GET /subscriptions/plans`, требует один из заголовков:\n\n* `Authorization: Bearer <JWT>` - access-токен из `POST /auth/login` (живёт 30 минут,\n  продлевается через `POST /auth/refresh`);\n* `X-API-Key: lfk_live_...` - API-ключ из `POST /auth/api-keys` (показывается один раз).\n\n## Ошибки\n\nТело ошибки: `{\"detail\": \"...\"}`. Коды: 401 - нет или недействителен токен/ключ,\n403 - нет доступа, 404 - объект не найден, 422 - ошибка валидации, 429 - превышен лимит\nзапросов, 502 - внутренний сервис недоступен.\n\n## Лимиты\n\nAuth-эндпоинты ограничены по IP (429, `Rate limit exceeded. Try again later.`).\nНа входе работает nginx: 10 запросов/с на IP, всплеск до 20.\n\n## Проксируемые методы\n\nПоиск, оплата, портфель, задачи и другие методы внутренних сервисов доступны по тому же\nхосту под `/api/v1/...`, но в эту схему не входят. Описание: `docs/api.md`.","version":"1.0"},"paths":{"/metrics":{"get":{"summary":"Metrics","description":"Endpoint that serves Prometheus metrics.","operationId":"metrics_metrics_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/register":{"post":{"tags":["auth"],"summary":"Register a new user","description":"Create a new user account with email and password. Returns access and refresh tokens along with user profile. A verification code is generated and sent to the user's email.","operationId":"register_api_v1_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"429":{"description":"Превышен лимит запросов, повторите позже","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/login":{"post":{"tags":["auth"],"summary":"Authenticate user","description":"Authenticate with email and password. Returns a new pair of access and refresh tokens. Invalid credentials return 401; unverified email returns 403.","operationId":"login_api_v1_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"429":{"description":"Превышен лимит запросов, повторите позже","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/refresh":{"post":{"tags":["auth"],"summary":"Refresh access token","description":"Exchange a valid refresh token for a new access token. Returns 401 if the refresh token is invalid or expired.","operationId":"refresh_api_v1_auth_refresh_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Refresh Api V1 Auth Refresh Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Logout current session","description":"Revoke the current access and refresh tokens. Requires a valid Bearer token in the Authorization header.","operationId":"logout_api_v1_auth_logout_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogoutRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auth/logout-all":{"post":{"tags":["auth"],"summary":"Terminate all sessions","description":"Revoke all refresh tokens for the authenticated user. Requires a valid Bearer token.","operationId":"logout_all_api_v1_auth_logout_all_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/verify-email":{"post":{"tags":["auth"],"summary":"Verify user email","description":"Verify the user's email address using a code sent during registration. In development mode, verification is always successful.","operationId":"verify_email_api_v1_auth_verify_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"429":{"description":"Превышен лимит запросов, повторите позже","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/resend-verification":{"post":{"tags":["auth"],"summary":"Resend the email verification code (internal services only)","description":"Issues a new verification code for a user whose email is not verified yet and sends it by email. Only for service-to-service calls (Telegram bot): the response contains the user id. 404 if the user does not exist or is already verified.","operationId":"resend_verification_api_v1_auth_resend_verification_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendVerificationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/forgot-password":{"post":{"tags":["auth"],"summary":"Request password reset","description":"Send a password reset link to the user's email. Always returns success to prevent email enumeration.","operationId":"forgot_password_api_v1_auth_forgot_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"429":{"description":"Превышен лимит запросов, повторите позже","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/reset-password":{"post":{"tags":["auth"],"summary":"Reset password with token","description":"Reset the user's password using a token from the reset email. Invalid or expired tokens return 400.","operationId":"reset_password_api_v1_auth_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"429":{"description":"Превышен лимит запросов, повторите позже","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/me":{"get":{"tags":["auth"],"summary":"Get current user profile","description":"Retrieve the authenticated user's profile information.","operationId":"get_me_api_v1_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfile"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]},"put":{"tags":["auth"],"summary":"Update current user profile","description":"Update the authenticated user's full name and/or email. At least one field must be provided.","operationId":"update_profile_api_v1_auth_me_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfile"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/change-password":{"post":{"tags":["auth"],"summary":"Change password","description":"Change the authenticated user's password. Requires the current password for verification. All other sessions are invalidated after password change; returns a new pair of tokens for the current session.","operationId":"change_password_api_v1_auth_change_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordChangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/login-methods":{"get":{"tags":["auth"],"summary":"Способы входа текущего пользователя","description":"Пароль, Telegram и привязанные соцсети (Яндекс, VK ID, MAX).","operationId":"get_login_methods_api_v1_auth_login_methods_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginMethodsResponse"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/login-methods/{provider}":{"delete":{"tags":["auth"],"summary":"Отвязать провайдера","description":"Отвязывает Яндекс, VK ID или MAX. Последний способ входа отвязать нельзя (409): сначала задайте пароль или привяжите другой способ. Telegram отвязывается через бота.","operationId":"unlink_login_method_api_v1_auth_login_methods__provider__delete","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginMethodsResponse"}}}},"404":{"description":"Провайдер не привязан"},"409":{"description":"Это последний способ входа"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/passkeys/register/options":{"post":{"tags":["auth"],"summary":"Начать добавление ключа доступа","description":"Параметры для navigator.credentials.create. Требует входа.","operationId":"passkey_register_options_api_v1_auth_passkeys_register_options_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyOptionsResponse"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/passkeys/register/verify":{"post":{"tags":["auth"],"summary":"Сохранить ключ доступа","description":"Проверяет ответ ключа и сохраняет его открытый ключ. 400 — ответ не прошёл проверку.","operationId":"passkey_register_verify_api_v1_auth_passkeys_register_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyRegisterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/passkeys":{"get":{"tags":["auth"],"summary":"Мои ключи доступа","operationId":"list_my_passkeys_api_v1_auth_passkeys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PasskeyInfo"},"type":"array","title":"Response List My Passkeys Api V1 Auth Passkeys Get"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/passkeys/{passkey_id}":{"delete":{"tags":["auth"],"summary":"Удалить ключ доступа","description":"Последний способ входа удалить нельзя (409).","operationId":"delete_my_passkey_api_v1_auth_passkeys__passkey_id__delete","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"passkey_id","in":"path","required":true,"schema":{"type":"string","title":"Passkey Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginMethodsResponse"}}}},"404":{"description":"Ключ не найден"},"409":{"description":"Это последний способ входа: сначала задайте пароль или добавьте другой способ"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/auth/passkeys/login/options":{"post":{"tags":["auth"],"summary":"Начать вход по ключу доступа","description":"Параметры для navigator.credentials.get. Email не нужен: браузер предложит ключи.","operationId":"passkey_login_options_api_v1_auth_passkeys_login_options_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyOptionsResponse"}}}}}}},"/api/v1/auth/passkeys/login/verify":{"post":{"tags":["auth"],"summary":"Войти по ключу доступа","description":"Проверяет ответ ключа и выдаёт токены, как обычный вход. 400 — проверка не прошла.","operationId":"passkey_login_verify_api_v1_auth_passkeys_login_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasskeyLoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/api-keys":{"get":{"tags":["auth"],"summary":"List my API keys","description":"Return all non-revoked API keys of the current user.","operationId":"list_api_keys_api_v1_auth_api_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ApiKeyInfo"},"type":"array","title":"Response List Api Keys Api V1 Auth Api Keys Get"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]},"post":{"tags":["auth"],"summary":"Create an API key","description":"Create a new universal API key (header X-API-Key). The full key value is returned only once and is not stored server-side.","operationId":"create_api_key_api_v1_auth_api_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/auth/api-keys/{key_id}":{"delete":{"tags":["auth"],"summary":"Revoke an API key","description":"Revoke the API key. Revoked keys stop working immediately.","operationId":"revoke_api_key_api_v1_auth_api_keys__key_id__delete","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/cases":{"post":{"tags":["cases"],"summary":"Создать дело","description":"Создать новое дело для анализа. После создания можно запустить анализ через ai-routing-service.","operationId":"create_case_api_v1_cases_post","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCaseRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"tags":["cases"],"summary":"Список дел пользователя","description":"Получить все дела текущего пользователя с фильтрацией по статусу.","operationId":"list_cases_api_v1_cases_get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Фильтр по статусу","title":"Status"},"description":"Фильтр по статусу"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CaseResponse"},"title":"Response List Cases Api V1 Cases Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/cases/{case_id}":{"get":{"tags":["cases"],"summary":"Получить дело","description":"Получить подробную информацию о деле по UUID.","operationId":"get_case_api_v1_cases__case_id__get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"case_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Case Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["cases"],"summary":"Обновить дело","description":"Обновить метаданные дела (ФИО клиента, номер, суд, статус и т.д.).","operationId":"update_case_api_v1_cases__case_id__put","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"case_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Case Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCaseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["cases"],"summary":"Удалить дело","description":"Удалить дело по UUID. Только для владельца дела.","operationId":"delete_case_api_v1_cases__case_id__delete","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"case_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Case Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/cases/{case_id}/aggregate":{"get":{"tags":["cases"],"summary":"Агрегировать данные дела (п.30 P2)","description":"Собирает данные дела из БД, storage-service (файлы) и llm-service (анализ) с circuit breaker'ом: при падении одного апстрима остальные секции возвращаются, сбойная помечается error.","operationId":"get_case_aggregate_api_v1_cases__case_id__aggregate_get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"case_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Case Id"}},{"name":"include_files","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Files"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/reports":{"get":{"tags":["reports"],"summary":"List user's reports","description":"Retrieve all analysis reports for the authenticated user. Reports are generated from completed analysis cases.","operationId":"list_reports_api_v1_reports_get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/reports/{report_id}":{"get":{"tags":["reports"],"summary":"Get report details","description":"Retrieve detailed information about a specific report by its ID.","operationId":"get_report_api_v1_reports__report_id__get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Report Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/reports/{report_id}/content":{"get":{"tags":["reports"],"summary":"Get report content","description":"Retrieve the full content of a specific report.","operationId":"get_report_content_api_v1_reports__report_id__content_get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$","title":"Report Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportContentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/users/link_telegram":{"post":{"tags":["users"],"summary":"Link Telegram account","description":"Link a Telegram account to the authenticated user. Enables Telegram notifications and bot interactions.","operationId":"link_telegram_api_v1_users_link_telegram_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkTelegramRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkTelegramResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/files/":{"get":{"tags":["files"],"summary":"List user's files","description":"Retrieve all files uploaded by the authenticated user. Returns file metadata including ID, filename, size, and upload date.","operationId":"get_files_api_v1_files__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileListResponse"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/files/{file_id}":{"get":{"tags":["files"],"summary":"Download a file","description":"Download a file by its ID. Returns a streaming response with the file content.","operationId":"download_file_api_v1_files__file_id__get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}}],"responses":{"200":{"description":"File content stream","content":{"application/json":{"schema":{}}}},"404":{"description":"File not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"tags":["files"],"summary":"Update a file","description":"Replace the content of an existing file with a new upload.","operationId":"update_file_api_v1_files__file_id__put","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_update_file_api_v1_files__file_id__put"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["files"],"summary":"Delete a file","description":"Delete a file by its ID. The file is permanently removed from storage.","operationId":"delete_file_api_v1_files__file_id__delete","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Объект не найден","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/files/upload":{"post":{"tags":["files"],"summary":"Upload a file","description":"Upload a new file to storage. Accepts multipart/form-data with a single file. The file is associated with the authenticated user.","operationId":"upload_file_api_v1_files_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_file_api_v1_files_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/sync":{"get":{"tags":["sync"],"summary":"Инкрементальная синхронизация дел и анализов","description":"Возвращает дела и результаты анализа текущего пользователя, отсортированные по `updated_at`. Ответ: `{cases, analyses, next_cursor, timestamp}`. Если записей больше `limit`, в `next_cursor` приходит ISO-метка для следующего запроса (параметр `cursor`).","operationId":"get_sync_data_api_v1_sync_get","security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}],"parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only return records updated after this timestamp","title":"Since"},"description":"Only return records updated after this timestamp"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":5000,"minimum":1,"default":500,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Cursor (ISO timestamp) for pagination","title":"Cursor"},"description":"Cursor (ISO timestamp) for pagination"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/docs":{"get":{"tags":["docs"],"summary":"Список сервисов платформы","description":"Список всех микросервисов платформы с метаданными OpenAPI.","operationId":"docs_index_api_v1_docs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/docs/{service}":{"get":{"tags":["docs"],"summary":"OpenAPI-схема сервиса","description":"Полная OpenAPI-схема конкретного сервиса платформы.","operationId":"service_docs_api_v1_docs__service__get","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","title":"Service"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"404":{"description":"Сервис не найден"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/marketing/referral":{"get":{"tags":["marketing"],"summary":"Get my referral info","description":"Мой реферальный код, ссылка для приглашения коллег и статистика бонусов.","operationId":"get_referral_api_v1_marketing_referral_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralInfoResponse"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/marketing/partner":{"get":{"tags":["marketing"],"summary":"Get partner program info","description":"Статистика партнёрской программы текущего юзера.","operationId":"get_partner_api_v1_marketing_partner_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerResponse"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/api/v1/marketing/partner/activate":{"post":{"tags":["marketing"],"summary":"Activate partner program","description":"Активировать партнёрскую программу (кнопка в профиле). Создаёт партнёра с уникальным кодом; клиенты по ссылке дают 25% с оплаты (до 3 оплат), суб-партнёрам 10% с их дохода.","operationId":"activate_partner_api_v1_marketing_partner_activate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerResponse"}}}},"401":{"description":"Токен или API-ключ отсутствует, недействителен или отозван","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{"HTTPBearer":[]},{"ApiKeyAuth":[]}]}},"/health":{"get":{"summary":"Health","description":"Health check: verify DB and Redis connectivity.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"ApiKeyInfo":{"properties":{"id":{"type":"string","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"prefix":{"type":"string","title":"Prefix"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"is_active":{"type":"boolean","title":"Is Active"}},"type":"object","required":["id","name","prefix","last_used_at","created_at","expires_at","is_active"],"title":"ApiKeyInfo","description":"API key metadata (full key value is shown only once on creation)."},"Body_update_file_api_v1_files__file_id__put":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"New file content"}},"type":"object","required":["file"],"title":"Body_update_file_api_v1_files__file_id__put"},"Body_upload_file_api_v1_files_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File","description":"File to upload"}},"type":"object","required":["file"],"title":"Body_upload_file_api_v1_files_upload_post"},"CaseDetailResponse":{"properties":{"id":{"type":"string","title":"Id","description":"UUID дела"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name","description":"ФИО клиента"},"case_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Case Number","description":"Номер дела"},"court_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Court Name","description":"Название суда"},"judge_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Judge Name","description":"Судья"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"Категория дела"},"status":{"type":"string","title":"Status","description":"Статус дела"},"outcome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Outcome","description":"Исход дела"},"created_at":{"type":"string","title":"Created At","description":"Дата создания"},"updated_at":{"type":"string","title":"Updated At","description":"Дата обновления"},"description":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Description","description":"Описание дела"}},"type":"object","required":["id","status","created_at","updated_at"],"title":"CaseDetailResponse","description":"Detailed case response with description."},"CaseResponse":{"properties":{"id":{"type":"string","title":"Id","description":"UUID дела"},"client_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name","description":"ФИО клиента"},"case_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Case Number","description":"Номер дела"},"court_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Court Name","description":"Название суда"},"judge_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Judge Name","description":"Судья"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","description":"Категория дела"},"status":{"type":"string","title":"Status","description":"Статус дела"},"outcome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Outcome","description":"Исход дела"},"created_at":{"type":"string","title":"Created At","description":"Дата создания"},"updated_at":{"type":"string","title":"Updated At","description":"Дата обновления"}},"type":"object","required":["id","status","created_at","updated_at"],"title":"CaseResponse","description":"Response model for a legal case."},"ChangePasswordRequest":{"properties":{"old_password":{"type":"string","title":"Old Password","description":"Current password"},"new_password":{"type":"string","minLength":8,"title":"New Password","description":"New password (min 8 characters)","examples":["newsecurepassword"]}},"type":"object","required":["old_password","new_password"],"title":"ChangePasswordRequest","description":"Request body for changing password while authenticated."},"CreateApiKeyRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Name","description":"Key label"}},"type":"object","title":"CreateApiKeyRequest","description":"Request body for creating an API key."},"CreateApiKeyResponse":{"properties":{"key":{"$ref":"#/components/schemas/ApiKeyInfo"},"raw_key":{"type":"string","title":"Raw Key","description":"Full API key value — shown once, not stored by the server"}},"type":"object","required":["key","raw_key"],"title":"CreateApiKeyResponse"},"CreateCaseRequest":{"properties":{"client_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Client Name","description":"ФИО клиента (фамилия + инициалы)","examples":["Иванов И.И."]},"case_number":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Case Number","description":"Номер дела","examples":["А40-12345/2024"]},"court_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Court Name","description":"Название суда","examples":["Арбитражный суд города Москвы"]},"judge_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Judge Name","description":"ФИО судьи","examples":["Иванов И.И."]},"category":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Category","description":"Категория дела","examples":["гражданское"]},"query":{"type":"string","maxLength":5000,"minLength":10,"title":"Query","description":"Описание дела для анализа","examples":["Взыскание убытков по договору поставки"]}},"type":"object","required":["query"],"title":"CreateCaseRequest","description":"Request body for creating a new legal case."},"FileDeleteResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Deletion status message"}},"type":"object","required":["message"],"title":"FileDeleteResponse","description":"Response after deleting a file."},"FileListResponse":{"properties":{"files":{"items":{"$ref":"#/components/schemas/FileMetadata"},"type":"array","title":"Files","description":"List of file metadata objects"}},"type":"object","required":["files"],"title":"FileListResponse","description":"Response containing list of files."},"FileMetadata":{"properties":{"id":{"type":"string","title":"Id","description":"File ID","examples":["file-123"]},"filename":{"type":"string","title":"Filename","description":"Original filename","examples":["contract.pdf"]},"size":{"type":"integer","title":"Size","description":"File size in bytes","examples":[1024000]},"content_type":{"type":"string","title":"Content Type","description":"MIME type","examples":["application/pdf"]},"created_at":{"type":"string","title":"Created At","description":"Upload timestamp","examples":["2024-01-15T10:30:00Z"]}},"type":"object","required":["id","filename","size","content_type","created_at"],"title":"FileMetadata","description":"Metadata for a stored file."},"FileUpdateResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Update status message"}},"type":"object","required":["message"],"title":"FileUpdateResponse","description":"Response after updating a file."},"FileUploadResponse":{"properties":{"file_id":{"type":"string","title":"File Id","description":"ID of the uploaded file","examples":["file-123"]}},"type":"object","required":["file_id"],"title":"FileUploadResponse","description":"Response after uploading a file."},"ForgotPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"User email address","examples":["user@example.com"]}},"type":"object","required":["email"],"title":"ForgotPasswordRequest","description":"Request body for initiating password reset."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LinkTelegramRequest":{"properties":{"telegram_id":{"type":"string","title":"Telegram Id","description":"Telegram user ID","examples":["123456789"]},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username","description":"Telegram username","examples":["ivan_petrov"]}},"type":"object","required":["telegram_id"],"title":"LinkTelegramRequest","description":"Request body for linking a Telegram account."},"LinkTelegramResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Status message"},"telegram_id":{"type":"string","title":"Telegram Id","description":"Linked Telegram user ID"}},"type":"object","required":["message","telegram_id"],"title":"LinkTelegramResponse","description":"Response after linking Telegram account."},"LoginIdentityInfo":{"properties":{"provider":{"type":"string","title":"Provider","description":"yandex, vk или max","examples":["yandex"]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Email у провайдера на момент привязки"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"last_login_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Login At"}},"type":"object","required":["provider","created_at","last_login_at"],"title":"LoginIdentityInfo"},"LoginMethodsResponse":{"properties":{"password":{"type":"boolean","title":"Password","description":"Пароль задан самим пользователем"},"telegram":{"type":"boolean","title":"Telegram","description":"Привязан Telegram (через бота)"},"identities":{"items":{"$ref":"#/components/schemas/LoginIdentityInfo"},"type":"array","title":"Identities"},"passkeys":{"type":"integer","title":"Passkeys","description":"Сколько ключей доступа (Passkeys) добавлено","default":0}},"type":"object","required":["password","telegram"],"title":"LoginMethodsResponse","description":"Чем пользователь может войти. Последний способ отвязать нельзя."},"LoginRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"User email address","examples":["user@example.com"]},"password":{"type":"string","title":"Password","description":"User password","examples":["securepassword"]}},"type":"object","required":["email","password"],"title":"LoginRequest","description":"Request body for user login."},"LogoutRequest":{"properties":{"refresh_token":{"type":"string","title":"Refresh Token","description":"Refresh token to revoke"}},"type":"object","required":["refresh_token"],"title":"LogoutRequest","description":"Request body for user logout."},"MessageResponse":{"properties":{"message":{"type":"string","title":"Message","description":"Status message"}},"type":"object","required":["message"],"title":"MessageResponse","description":"Generic message response."},"PartnerResponse":{"properties":{"is_partner":{"type":"boolean","title":"Is Partner","description":"Активен ли партнёр"},"partner_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner Code","description":"Код партнёра для рефссылки"},"referral_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referral Link","description":"Путь-приглашение /register?partner=CODE"},"invited_clients":{"type":"integer","title":"Invited Clients","description":"Сколько клиентов приведено","default":0},"paid_payments":{"type":"integer","title":"Paid Payments","description":"Сколько оплат клиентов уже принесло доход","default":0},"max_payments":{"type":"integer","title":"Max Payments","description":"Лимит оплат клиента, с которого начисляется доход","default":3},"total_earned":{"type":"number","title":"Total Earned","description":"Сумма начислений всего","default":0},"pending_amount":{"type":"number","title":"Pending Amount","description":"Начислено, но ещё в рефанд-окне","default":0},"payable_amount":{"type":"number","title":"Payable Amount","description":"Доступно к выплате","default":0}},"type":"object","required":["is_partner"],"title":"PartnerResponse","description":"Программа партнёрства: активация, ссылка, статистика."},"PasskeyInfo":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"backed_up":{"type":"boolean","title":"Backed Up","description":"Ключ синхронизируется (iCloud, Google)"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"}},"type":"object","required":["id","name","backed_up","created_at","last_used_at"],"title":"PasskeyInfo"},"PasskeyLoginRequest":{"properties":{"challenge_id":{"type":"string","title":"Challenge Id"},"credential":{"type":"object","title":"Credential","description":"Ответ navigator.credentials.get"}},"type":"object","required":["challenge_id","credential"],"title":"PasskeyLoginRequest"},"PasskeyOptionsResponse":{"properties":{"challenge_id":{"type":"string","title":"Challenge Id","description":"Вернуть в verify вместе с ответом ключа"},"options":{"type":"object","title":"Options","description":"PublicKeyCredentialCreation/RequestOptions в JSON для браузера"}},"type":"object","required":["challenge_id","options"],"title":"PasskeyOptionsResponse"},"PasskeyRegisterRequest":{"properties":{"challenge_id":{"type":"string","title":"Challenge Id"},"credential":{"type":"object","title":"Credential","description":"Ответ navigator.credentials.create"},"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name","examples":["iPhone"]}},"type":"object","required":["challenge_id","credential","name"],"title":"PasskeyRegisterRequest"},"PasswordChangeResponse":{"properties":{"access_token":{"type":"string","title":"Access Token","description":"Новый JWT access token"},"refresh_token":{"type":"string","title":"Refresh Token","description":"Новый refresh token"},"token_type":{"type":"string","title":"Token Type","description":"Token type","default":"bearer"}},"type":"object","required":["access_token","refresh_token"],"title":"PasswordChangeResponse","description":"Новая пара токенов после смены пароля (профиль пользователя не возвращается).\n\nОтдельная модель, а не TokenResponse: у того поле ``user`` обязательное, и ручка, которая\nего не отдаёт, после успешной смены пароля падала с 500 на валидации ответа."},"ReferralInfoResponse":{"properties":{"referral_code":{"type":"string","title":"Referral Code","description":"Мой реферальный код"},"referral_link":{"type":"string","title":"Referral Link","description":"Путь-приглашение /register?ref=CODE"},"invited_count":{"type":"integer","title":"Invited Count","description":"Сколько коллег приглашено","default":0},"bonuses_received":{"type":"integer","title":"Bonuses Received","description":"Сколько бонусных месяцев начислено","default":0}},"type":"object","required":["referral_code","referral_link"],"title":"ReferralInfoResponse","description":"Реферальная программа (пригласить коллегу)."},"RefreshRequest":{"properties":{"refresh_token":{"type":"string","title":"Refresh Token","description":"Refresh token received during login/register"}},"type":"object","required":["refresh_token"],"title":"RefreshRequest","description":"Request body for token refresh."},"RegisterRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"User email address","examples":["user@example.com"]},"password":{"type":"string","minLength":8,"title":"Password","description":"Password (min 8 characters)","examples":["securepassword"]},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name","description":"Full display name","examples":["Ivan Petrov"]},"ref":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ref","description":"Referral code","examples":["ABCD1234"]},"partner_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner Code","description":"Partner code","examples":["PARTNER_ABC"]},"consent_privacy":{"type":"boolean","title":"Consent Privacy","description":"Согласие с политикой конфиденциальности","default":false},"consent_data_processing":{"type":"boolean","title":"Consent Data Processing","description":"Согласие с политикой обработки персональных данных","default":false}},"type":"object","required":["email","password"],"title":"RegisterRequest","description":"Request body for user registration."},"ReportContentResponse":{"properties":{"report_id":{"type":"string","title":"Report Id","description":"Report ID"},"content":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Content","description":"Report JSON content"},"format":{"type":"string","title":"Format","description":"Content format","default":"json"}},"type":"object","required":["report_id"],"title":"ReportContentResponse","description":"Full content of a report."},"ReportListResponse":{"properties":{"reports":{"items":{"$ref":"#/components/schemas/ReportResponse"},"type":"array","title":"Reports","description":"List of reports"},"total":{"type":"integer","title":"Total","description":"Total number of reports","examples":[42]}},"type":"object","required":["reports","total"],"title":"ReportListResponse","description":"Paginated list of reports."},"ReportResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Unique report identifier","examples":["550e8400-e29b-41d4-a716-446655440000"]},"case_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Case Id","description":"Associated case ID","examples":["550e8400-e29b-41d4-a716-446655440001"]},"title":{"type":"string","title":"Title","description":"Report title","examples":["Обзор практики по банкротству"]},"status":{"type":"string","title":"Status","description":"Report status","examples":["completed"]},"created_at":{"type":"string","title":"Created At","description":"Creation timestamp","examples":["2024-01-15T10:30:00Z"]}},"type":"object","required":["id","title","status","created_at"],"title":"ReportResponse","description":"Response model for a generated analysis report."},"ResendVerificationRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email","description":"Email of the not yet verified user"}},"type":"object","required":["email"],"title":"ResendVerificationRequest","description":"Request body for resending the verification code."},"ResetPasswordRequest":{"properties":{"token":{"type":"string","title":"Token","description":"Password reset token from email"},"new_password":{"type":"string","minLength":8,"title":"New Password","description":"New password (min 8 characters)","examples":["newsecurepassword"]}},"type":"object","required":["token","new_password"],"title":"ResetPasswordRequest","description":"Request body for resetting password with a token."},"TokenResponse":{"properties":{"access_token":{"type":"string","title":"Access Token","description":"JWT access token"},"refresh_token":{"type":"string","title":"Refresh Token","description":"Refresh token for obtaining new access tokens"},"token_type":{"type":"string","title":"Token Type","description":"Token type","default":"bearer"},"user":{"type":"object","title":"User","description":"User profile info","examples":[{"email":"user@example.com","full_name":"Ivan Petrov","id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","role":"user"}]}},"type":"object","required":["access_token","refresh_token","user"],"title":"TokenResponse","description":"Response containing authentication tokens and user info."},"UpdateCaseRequest":{"properties":{"client_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Client Name"},"case_number":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Case Number"},"court_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Court Name"},"judge_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Judge Name"},"category":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Category"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"pending, in_progress, won, lost, settled","examples":["in_progress"]},"outcome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Outcome","description":"Исход дела","examples":["выиграно"]}},"type":"object","title":"UpdateCaseRequest","description":"Request body for updating a case."},"UpdateProfileRequest":{"properties":{"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name","description":"New full name","examples":["Ivan Petrov"]},"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email","description":"New email address","examples":["newuser@example.com"]}},"type":"object","title":"UpdateProfileRequest","description":"Request body for updating user profile."},"UserProfile":{"properties":{"id":{"type":"string","title":"Id","description":"User ID"},"email":{"type":"string","title":"Email","description":"User email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name","description":"User full name"},"role":{"type":"string","title":"Role","description":"User role"}},"type":"object","required":["id","email","role"],"title":"UserProfile","description":"User profile response."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerifyEmailRequest":{"properties":{"code":{"type":"string","title":"Code","description":"Verification code sent to user email (8 hex chars)"}},"type":"object","required":["code"],"title":"VerifyEmailRequest","description":"Request body for email verification."},"ErrorResponse":{"description":"Единый формат ошибки FastAPI/gateway.","properties":{"detail":{"description":"Описание ошибки","examples":["Invalid token"],"title":"Detail","type":"string"}},"required":["detail"],"title":"ErrorResponse","type":"object"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"},"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API-ключ вида `lfk_live_...`, создаётся через `POST /api/v1/auth/api-keys`."}}},"tags":[{"name":"auth","description":"Регистрация, вход, токены, профиль и API-ключи."},{"name":"cases","description":"Дела пользователя: создание, список, просмотр, удаление."},{"name":"reports","description":"Отчёты по анализу дел."},{"name":"files","description":"Загрузка и скачивание файлов (multipart, поле `file`)."},{"name":"sync","description":"Инкрементальная выгрузка дел и анализов для клиентов."},{"name":"users","description":"Привязка внешних аккаунтов (Telegram)."},{"name":"marketing","description":"Реферальная и партнёрская программы."},{"name":"docs","description":"Реестр и схема OpenAPI самого api-gateway."}]}