> For the complete documentation index, see [llms.txt](https://finext.gitbook.io/one-kyc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://finext.gitbook.io/one-kyc/admin/review.md).

# Review

The **Review** section of the admin console handles verification sessions routed to manual review. Reviewers inspect documents and applicant data, decide approval or rejection, and leave internal comments.

> **Integrators (server-to-server):** the same queue is available on the Tenant API under `/tenant/v1/kyc/review-tasks` and `/tenant/v1/kyb/review-tasks` with scopes `reviews:*` / `kyb.reviews:*`. See [Review tasks (Tenant API)](/one-kyc/api/review.md).
>
> Admin console HTTP paths below are relative to the admin API (`/admin/v1/...`), not the Tenant API.

## Task queue

The queue lists tasks (sessions) waiting for manual review or in progress. Each task shows the session identifier, queue arrival date, current status, and the assigned reviewer.

### Filters

To manage the queue, the following filters are available:

* **By task status** — new (waiting for assignment), in progress, approved, rejected.
* **By assigned reviewer** — tasks for a specific reviewer or unassigned tasks.
* **By date** — task arrival date range.

### Queue statistics

The section header shows summary metrics:

* Number of tasks waiting for assignment.
* Number of tasks in progress.
* Total number of tasks processed in the current period.

## Working on a task

When you open a task, you have access to the following information:

### Session and applicant data

* Session and applicant identifier.
* Data extracted from the document (full name, date of birth, document number — when OCR is available).
* External user identifier (external\_user\_id).

### Documents

* Uploaded document and selfie images, viewable at full size.
* Automated check results: document authenticity, image quality, face match.

### Check history

* Chronological order of verification step execution.
* Automated check results (document check, liveness, face match, AML).

## Reviewer actions

### Task assignment

* **Assign to me** — the reviewer takes the task. The status changes to "In progress".
* **Reassign** — transfer the task to another reviewer (for example, when a reviewer with broader permissions is needed).

### Decision

* **Approve** — verification passes. The session receives the `approved` status.
* **Reject** — verification fails. The session receives the `rejected` status. On rejection, you must:
  1. Pick a **rejection reason** from the structured list (`GET /admin/v1/review/reject-reasons`), for example `blurry_document`, `face_not_matching`, `suspected_fraud`, or `expired_document`.
  2. Optionally enter an **additional comment** with an explanation.

Canonical reject codes are the same as on the Tenant API (`blurry_document`, `unreadable_document`, `face_not_matching`, … — full list in [Review tasks](/one-kyc/api/review.md#stats-and-reject-reasons)).

### Changing task priority

Through `PATCH /admin/v1/review/tasks/{task_id}/priority`, a reviewer with the right permission can change the task priority (for example, urgent VIP cases). The action is logged as `review.priority_updated`.

### Delegating a task

Through `POST /admin/v1/review/tasks/{task_id}/delegate`, a reviewer can delegate the task to the OneKYC manual-review team. The current delegation status is returned by `GET /admin/v1/review/tasks/{task_id}/delegation-status`.

### Comments

Reviewers can add an internal comment through `POST /admin/v1/review/tasks/{task_id}/comments`. Comments are visible to other reviewers and stored in the task history for audit and context handover.

### Accessing uploaded documents

To view a specific applicant document, use `GET /admin/v1/review/tasks/{task_id}/documents/{document_id}/url` — it returns a signed URL with a limited lifetime.

### Queue statistics

Aggregate review-task metrics (queue size, in progress, processed for a period) are available through `GET /admin/v1/review/stats` (or `GET /admin/v1/review/queue` with scope `kyc` / `kyb`).

## Resubmit requests

Sometimes automated checks pass, but during manual review a document field turns out to be unreadable (glare, blur, cropped frame, wrong document, expired, etc.). In that case you can **ask the user to resubmit a specific step** without rejecting the whole session. This preserves conversion and saves the user from running through verification from scratch.

### Use case

1. The reviewer opens a task in the review queue and sees that the front-side document photo is blurry.
2. Instead of rejecting, the reviewer clicks **"Request resubmit"**, picks the step (for example, `document_capture`) and the reason code (`blurry`).
3. The session moves to **awaiting\_resubmit**, and the selected step is reset.
4. The user receives a notification and reopens the flow — they go straight to the reset step with the reason explained.
5. After successful resubmission, the session returns to the review queue automatically.

### Reason codes (`reason_code`)

The allowed reason codes are:

| Code               | Description                    |
| ------------------ | ------------------------------ |
| `illegible`        | Text is unreadable             |
| `blurry`           | Image is blurred               |
| `cropped`          | Document is cropped            |
| `glare`            | Glare obscures the data        |
| `wrong_document`   | Wrong document type uploaded   |
| `expired_document` | Document has expired           |
| `partial`          | Document is not fully captured |

The same codes are used in webhook payloads and in the message shown to the user.

### Limits and TTL

* **Up to 3 requests per session.** A fourth attempt returns `409 Conflict` with code `resubmit_limit_exceeded`.
* **TTL — 7 days.** After that, the request transitions to `expired`, and the session closes with the final status `rejected` (if no other channel completed it).
* **No more than one active request per (session, step) pair at a time.** A duplicate attempt returns `409 Conflict` with code `resubmit_already_pending`.
* **The session must be active for review resubmit.** `in_progress` sessions are accepted. A completed session can also be accepted when it is in the manual-review/resubmit path; the backend reopens it and resets the selected step atomically. Requests against pending, paused, failed, cancelled, or expired sessions return `409 Conflict` with code `session_not_in_progress`.
* **Only applicant-actionable step types can be resubmitted.** The current allowlist is `document`, `selfie`, `phone_otp`, `email_otp`, `kyb_company_input`, `kyb_business_document`, and `kyb_beneficiary_kyc`.

### Endpoints

#### Create a resubmit request

```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": "Please re-take the document in daylight without glare",
  "internal_notes": "Date of birth field is unreadable due to glare"
}
```

* `step_id` — UUID of the specific flow step to reset.
* `reason_code` — required, from the canonical list above.
* `client_message` (optional) — text shown to the user.
* `internal_notes` (optional) — internal note for other reviewers; not shown to the user.

Response (`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"
  }
}
```

The action is logged in the audit trail (`review.decided` + `decision=request_resubmit` + `reason_code`).

#### List task resubmit requests

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

Returns every resubmit request for the task (including fulfilled / expired / cancelled), sorted by creation date.

### Request lifecycle

| Status      | Description                                                            |
| ----------- | ---------------------------------------------------------------------- |
| `pending`   | Request created, sent to the user, waiting for resubmission            |
| `fulfilled` | The user resubmitted the step — the task is back in the review queue   |
| `expired`   | 7 days passed without resubmission — the session closes with rejection |
| `cancelled` | Request cancelled by the admin / tenant before fulfilment              |

The `pending → fulfilled` transition happens automatically after the user completes the requested step again.

### User notification delivery

OneKYC delivers the resubmit notification through the available user channels:

1. **WebSocket push** — instant, when the user has the verification flow open.
2. **Email / SMS** — sent when the user needs a session-resume link outside the open verification page.

### Webhook events

When a request is created or fulfilled, OneKYC sends webhook events:

* **`verification.resubmit_requested`** — a reviewer requested resubmission. The payload includes `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`** — the user resubmitted. The payload includes `session_id`, `flow_id`, `applicant_id`, `step_id`, `reason_code`, `attempt_number`, `fulfilled_at`.

Full payload schemas are in [Webhooks API](/one-kyc/api/webhooks.md#manual-review-resubmit-events-verificationresubmit_).

## Outcome of processing

After a decision is made:

* The session receives a final status (`approved` or `rejected`).
* The result is available through the Tenant API and is sent to configured [webhooks](/one-kyc/api/webhooks.md) (events `verification.approved` / `verification.declined`).
* The decision, rejection reason, and comments are recorded in the [audit log](/one-kyc/admin/audit.md).
