# Ревью (ручная проверка)

Раздел **Ревью** админ-панели предназначен для обработки верификационных сессий, направленных на ручную проверку. Операторы просматривают документы и данные аппликантов, выносят решения об одобрении или отклонении верификации, оставляют внутренние комментарии.

> **Интеграторы (server-to-server):** та же очередь доступна в Tenant API: `/tenant/v1/kyc/review-tasks` и `/tenant/v1/kyb/review-tasks` (scopes `reviews:*` / `kyb.reviews:*`). См. [Задачи ревью (Tenant API)](/ru/api/review).
>
> Пути ниже относятся к admin API (`/admin/v1/...`), не к Tenant API.

## Очередь задач [#очередь-задач]

Очередь отображает список задач (сессий), ожидающих ручной проверки или находящихся в работе. Для каждой задачи указаны: идентификатор сессии, дата поступления, текущий статус и назначенный оператор.

### Фильтрация [#фильтрация]

Для управления очередью доступны следующие фильтры:

- **По статусу задачи** — новая (ожидает назначения), в работе, одобрена, отклонена.
- **По назначенному оператору** — отображение задач конкретного оператора или нераспределённых задач.
- **По дате** — выбор временного диапазона поступления задач.

### Статистика очереди [#статистика-очереди]

В заголовке раздела отображаются сводные показатели:

- Количество задач, ожидающих назначения.
- Количество задач в работе.
- Общее число обработанных задач за текущий период.

## Работа с задачей [#работа-с-задачей]

При открытии задачи оператор получает доступ к следующей информации:

### Данные сессии и аппликанта [#данные-сессии-и-аппликанта]

- Идентификатор сессии и аппликанта.
- Данные, извлечённые из документа (ФИО, дата рождения, номер документа — при наличии OCR).
- Внешний идентификатор пользователя (external\_user\_id).

### Документы [#документы]

- Загруженные изображения документов и селфи с возможностью просмотра в полном размере.
- Результаты автоматических проверок: подлинность документа, качество изображения, соответствие лица.

### История проверок [#история-проверок]

- Хронология прохождения шагов верификации.
- Результаты автоматических проверок (document check, liveness, face match, AML).

## Действия оператора [#действия-оператора]

### Назначение задачи [#назначение-задачи]

- **Назначить на себя** — оператор берёт задачу в работу. Статус задачи изменяется на «В работе».
- **Переназначить** — передача задачи другому оператору (например, при необходимости привлечения специалиста с расширенными полномочиями).

### Вынесение решения [#вынесение-решения]

- **Одобрить** — верификация считается пройденной. Сессия получает статус `approved`.
- **Отклонить** — верификация не пройдена. Сессия получает статус `rejected`. При отклонении необходимо:
  1. Выбрать **причину отклонения** из структурированного списка (`GET /admin/v1/review/reject-reasons`), например `blurry_document`, `face_not_matching`, `suspected_fraud` или `expired_document`.
  2. При необходимости ввести **дополнительный комментарий** с пояснением.

Канонические reject-коды совпадают с Tenant API (полный список: [Задачи ревью](/ru/api/review#stats-i-prichiny-otkloneniya)).

### Изменение приоритета задачи [#изменение-приоритета-задачи]

Через `PATCH /admin/v1/review/tasks/{task_id}/priority` оператор с соответствующими правами может изменить приоритет задачи (например, для срочных VIP-кейсов). Действие фиксируется в журнале аудита как `review.priority_updated`.

### Делегирование задачи [#делегирование-задачи]

Через `POST /admin/v1/review/tasks/{task_id}/delegate` оператор может делегировать задачу команде OneKYC. Текущий статус делегирования: `GET /admin/v1/review/tasks/{task_id}/delegation-status`.

### Комментарии [#комментарии]

Оператор может добавить внутренний комментарий через `POST /admin/v1/review/tasks/{task_id}/comments`. Комментарии доступны другим операторам и сохраняются в истории задачи для целей аудита и передачи контекста.

### Доступ к загруженным документам [#доступ-к-загруженным-документам]

Для просмотра конкретного документа аппликанта используется `GET /admin/v1/review/tasks/{task_id}/documents/{document_id}/url` — возвращается подписанная ссылка с ограниченным сроком действия.

### Статистика по очереди [#статистика-по-очереди]

Сводные показатели по задачам ревью доступны через `GET /admin/v1/review/stats` (или очередь `GET /admin/v1/review/queue` с scope `kyc` / `kyb`).

## Запрос пересдачи (Resubmit Requests) [#запрос-пересдачи-resubmit-requests]

Если автоматические проверки прошли, но при ручном просмотре оператор обнаруживает, что какое-то поле документа нечитаемо (блики, размытие, обрезанный кадр, не тот документ, просрочен и т. п.), он может **запросить у пользователя повторную загрузку конкретного шага**, не отклоняя сессию целиком. Это сохраняет конверсию и избавляет пользователя от повторного прохождения всей верификации с нуля.

### Сценарий использования [#сценарий-использования]

1. Оператор открывает задачу в очереди ревью и видит, что фото лицевой стороны документа размыто.
2. Вместо отклонения оператор нажимает **«Запросить пересдачу»**, выбирает шаг (например, `document_capture`) и код причины (`blurry`).
3. Сессия переходит в состояние **awaiting_resubmit**, выбранный шаг сбрасывается.
4. Пользователь получает уведомление и открывает flow повторно — он попадает сразу на сброшенный шаг с пояснением причины.
5. После успешной повторной загрузки сессия автоматически возвращается в очередь ревью.

### Коды причин (`reason_code`) [#коды-причин-reason_code]

Доступные коды причин:

| Код | Описание |
|-----|----------|
| `illegible` | Текст нечитаем |
| `blurry` | Изображение размытое |
| `cropped` | Документ обрезан |
| `glare` | Блики перекрывают данные |
| `wrong_document` | Загружен неверный тип документа |
| `expired_document` | Срок действия документа истёк |
| `partial` | Документ загружен не полностью |

Эти же коды используются в webhook payload и в сообщении, которое видит пользователь.

### Лимиты и TTL [#лимиты-и-ttl]

- **По умолчанию не более 3 запросов на сессию.** При попытке создать четвёртый возвращается `409 Conflict` с кодом `conflict`.
- После развёртывания соответствующих версии backend и схемы утверждённым изменением БД можно задать `flow_review_tasks.resubmit_limit_override` от 4 до 10 для одной задачи. Изменение нужно зафиксировать в операционном аудите. `NULL` сохраняет лимит по умолчанию. История пересдач остаётся нетронутой; считаются запросы в любом статусе.
- **TTL — 7 дней.** По истечении этого срока запрос автоматически переводится в статус `expired`, а сессия закрывается с финальным статусом `rejected` (если других каналов завершения не было).
- **Не более одного активного запроса на пару (session, step) одновременно.** Повторная попытка возвращает `409 Conflict` с кодом `resubmit_already_pending`.
- **Сессия должна быть активна для resubmit в ручном ревью.** Сессии в `in_progress` принимаются. Завершённая сессия также может быть принята, если она находится в сценарии ручного ревью/resubmit; backend атомарно переоткрывает её и сбрасывает выбранный шаг. Запросы к `pending`, `paused`, `failed`, `cancelled` или `expired` возвращают `409 Conflict` с кодом `session_not_in_progress`.
- **Повторно сдавать можно только шаги, где пользователь может выполнить действие.** Текущий allowlist: `document`, `selfie`, `phone_otp`, `email_otp`, `kyb_company_input`, `kyb_business_document`, `kyb_beneficiary_kyc`.

### Endpoints [#endpoints]

#### Создать запрос пересдачи [#создать-запрос-пересдачи]

```http
POST /admin/v1/review/tasks/{task_id}/request-resubmit
Content-Type: application/json

{
  "step_id": "550e8400-e29b-41d4-a716-446655440000",
  "reason_code": "blurry",
  "client_message": "Пожалуйста, переснимите документ при дневном свете без бликов",
  "internal_notes": "Поле даты рождения нечитаемо из-за бликов"
}
```

- `step_id` — UUID конкретного шага flow, который нужно сбросить.
- `reason_code` — обязательное поле из канонического списка выше.
- `client_message` (опц.) — текст, который увидит пользователь.
- `internal_notes` (опц.) — внутренняя заметка для других операторов; не отображается пользователю.

Ответ (`200 OK`):

```json
{
  "data": {
    "request_id": "550e8400-e29b-41d4-a716-446655440099",
    "task_id": "550e8400-e29b-41d4-a716-446655440010",
    "status": "pending",
    "reason_code": "blurry",
    "expires_at": "2026-05-04T10:00:00Z"
  }
}
```

Действие фиксируется в журнале аудита (`review.decided` + `decision=request_resubmit` + `reason_code`).

#### Получить список запросов на задачу [#получить-список-запросов-на-задачу]

```http
GET /admin/v1/review/tasks/{task_id}/resubmits
```

Возвращает все запросы пересдачи на данную задачу (включая завершённые / истёкшие / отменённые), отсортированные по дате создания.

### Жизненный цикл запроса [#жизненный-цикл-запроса]

| Статус | Описание |
|--------|----------|
| `pending` | Запрос создан, отправлен пользователю, ожидает повторной загрузки |
| `fulfilled` | Пользователь успешно повторно загрузил шаг — задача снова в очереди ревью |
| `expired` | Прошло 7 дней без повторной загрузки — сессия закрывается отклонением |
| `cancelled` | Запрос отменён администратором / тенантом до выполнения |

Переход `pending → fulfilled` происходит автоматически после того, как пользователь повторно проходит запрошенный шаг.

### Доставка уведомлений пользователю [#доставка-уведомлений-пользователю]

OneKYC доставляет уведомление о запросе пересдачи через доступные пользователю каналы:

1. **WebSocket push** — мгновенно, если у пользователя открыт флоу верификации.
2. **Email / SMS** — отправляется, когда пользователю нужна ссылка для возврата к сессии вне открытой страницы верификации.

### Webhook-события [#webhook-события]

При создании / выполнении запроса OneKYC отправляет webhook-события:

- **`verification.resubmit_requested`** — оператор запросил повторную загрузку. Payload включает `session_id`, `flow_id`, `applicant_id`, `step_id`, `step_type`, `reason_code`, `client_message`, `attempt_number`, `requested_by_id`, `requested_by_name`, `expires_at`.
- **`verification.resubmit_fulfilled`** — пользователь выполнил повторную загрузку. Payload включает `session_id`, `flow_id`, `applicant_id`, `step_id`, `reason_code`, `attempt_number`, `fulfilled_at`.

Полные схемы payload — в разделе [Вебхуки API](/ru/api/webhooks#события-manual-review-resubmit-verification.resubmit_).

## Результат обработки [#результат-обработки]

После вынесения решения:

- Сессия получает финальный статус (`approved` или `rejected`).
- Результат доступен через Tenant API и отправляется на настроенные [вебхуки](/ru/admin/webhooks) (события `verification.approved` / `verification.declined`).
- Решение, причина отклонения и комментарии фиксируются в [журнале аудита](/ru/admin/audit).

## Вебхуки комментариев ревью [#вебхуки-комментариев-ревью]

Новый комментарий к задаче KYC создаёт событие `verification.comment.created`, к задаче KYB — `kyb.review.comment.created`. Одинаковый контракт действует для админки клиента, платформенной консоли ревью и клиентского API. Комментарий не меняет решение проверки и не завершает сессию.

Подпишитесь на нужный тип события. Существующие подписки `*` также получают новые события. Используются обычные подпись вебхука, фильтрация sandbox/production, повторные попытки и журнал доставок. Возможны повторные доставки: устраняйте дубли по `id` события или `idempotency_key`. Порядок получения не гарантирован.

Поле `data` содержит `scope`, `task_id`, `session_id`, `flow_id`, `external_id` и объект `comment` с полями `id`, `content`, `created_at`, `author_type`. Для KYC передаётся `applicant_id`, для KYB — `business_id`, если он задан. Типы авторов: `admin_user`, `superadmin_user`, `api_key`, `system`. Идентификаторы и имена сотрудников в вебхук не включаются.

Пример данных события для KYC:

```json
{
  "scope": "kyc",
  "task_id": "550e8400-e29b-41d4-a716-446655440010",
  "session_id": "550e8400-e29b-41d4-a716-446655440001",
  "flow_id": "550e8400-e29b-41d4-a716-446655440002",
  "external_id": "customer_123",
  "applicant_id": "550e8400-e29b-41d4-a716-446655440099",
  "comment": {
    "id": "550e8400-e29b-41d4-a716-446655440011",
    "content": "Please provide a clearer document photo.",
    "created_at": "2026-10-16T08:00:00.123456Z",
    "author_type": "admin_user"
  }
}
```

Все комментарии задачи доступны клиенту-интегратору. Внутренние заметки решения `internal_notes` и сообщение повторной загрузки `client_message` — отдельные поля; они не создают события комментариев. События создаются только для новых комментариев; исторические комментарии не рассылаются.

Получение комментариев: `GET /tenant/v1/kyc/review-tasks/{task_id}/comments` с правом `reviews:read` или `GET /tenant/v1/kyb/review-tasks/{task_id}/comments` с правом `kyb.reviews:read`. Создание: `POST` по тому же пути с соответствующим правом записи. Успешный ответ создания дополнен `comment_id` и `created_at`, совпадающими с сохранённым комментарием и снимком в вебхуке. Каждый успешный POST создаёт новый комментарий; дедупликация вебхука не делает повторные запросы создания идемпотентными. При неопределённом результате запроса сверяйтесь с API комментариев до повторной отправки.

Комментарий, событие и запись outbox сохраняются одной транзакцией. Ошибка сохранения события откатывает создание комментария. HTTP-доставка выполняется асинхронно после фиксации транзакции: успешный ответ создания не подтверждает доставку вебхука клиенту; проверяйте журнал доставок.
