# Review tasks (manual review API)

Tenant API methods for **server-side manual review**: list the queue, inspect a task, assign it to the calling API key, approve or reject, comment, change priority, request a step resubmit, and fetch document URLs.

These endpoints are intended for **integrators** that run review outside the admin console (their own back-office or automation). Admin-panel operators use the console UI; see [Admin → Review](/en/admin/review).

Base paths:

| Product | Base |
| --- | --- |
| KYC | `/tenant/v1/kyc/review-tasks` |
| KYB | `/tenant/v1/kyb/review-tasks` |

Authentication: HMAC-SHA256 with a KYC or KYB API key. See [Authentication](/en/api/authentication).

## Required scopes [#required-scopes]

| Scope | Access |
| --- | --- |
| `reviews:read` | KYC: list queue, stats, reject reasons, task detail, comments, resubmits |
| `reviews:write` | KYC: assign, approve, reject, comment, priority, request resubmit |
| `media:read` | KYC: document presigned URLs (together with `reviews:read`) |
| `kyb.reviews:read` | KYB read surface |
| `kyb.reviews:write` | KYB write surface |
| `kyb.media:read` | KYB document URLs (together with `kyb.reviews:read`) |

KYC keys cannot call `/kyb/review-tasks/*` (HTTP 403). KYB keys cannot call `/kyc/review-tasks/*`.

Mutations are attributed to the **API key** (`actor_type=api_key`): assignment, comments, and final decisions store the key id and display name.

Response fields for actor identity (when present):

| Resource | Fields |
| --- | --- |
| Queue item / task detail | `assigned_to`, `assigned_to_type` (`admin_user` \| `superadmin_user` \| `api_key`), `assigned_display_name` |
| Task detail after decision | `decision_by`, `decision_by_type`, `decision_display_name` |
| Comment | `author_id`, `author_type`, `author_display_name` |

## Typical flow [#typical-flow]

1. Subscribe to `verification.needs_review` (and related webhooks) — see [Webhooks](/en/api/webhooks).
2. `GET .../review-tasks` — pull the queue (`status=pending` or `in_progress`).
3. `GET .../review-tasks/{task_id}` — load detail and `review_reasons`.
4. `POST .../assign` — claim the task for this API key.
5. Inspect media via `GET .../documents/{document_id}/url` when needed.
6. Decide:
   - `POST .../approve`
   - `POST .../reject` with a **canonical** `reason_code` from `GET .../reject-reasons` or a nonblank own `comment`
   - or `POST .../request-resubmit` for a single step (limits apply)

After a final decision, webhooks emit the terminal verification outcome (`verification.approved` / `verification.declined` / session completed).

## List queue [#list-queue]

API operations:

- `GET /v1/kyc/review-tasks`

KYB equivalent: `GET /tenant/v1/kyb/review-tasks` (same filters; search is company/external id oriented).

### Status filter aliases [#status-filter-aliases]

| Query value | Stored statuses |
| --- | --- |
| `in_progress` | `assigned` |
| `completed` | `approved`, `rejected` |
| other values | passed through (`pending`, `awaiting_resubmit`, …) |

### Priority values [#priority-values]

| Wire value | Numeric priority |
| --- | --- |
| `low` | 1 |
| `medium` | 2 |
| `high` | 3 |
| `urgent` | 4 |

## Stats and reject reasons [#stats-and-reject-reasons]

API operations:

- `GET /v1/kyc/review-tasks/stats`

API operations:

- `GET /v1/kyc/review-tasks/reject-reasons`

Canonical rejection codes (must be used on `POST .../reject`):

| Code | Category |
| --- | --- |
| `blurry_document` | document |
| `expired_document` | document |
| `wrong_document_type` | document |
| `document_mismatch` | document |
| `unreadable_document` | document |
| `face_not_matching` | biometric |
| `poor_selfie_quality` | biometric |
| `liveness_failed` | biometric |
| `face_obscured` | biometric |
| `sanctions_match` | aml |
| `pep_match` | aml |
| `adverse_media` | aml |
| `suspected_fraud` | general |
| `duplicate_account` | general |
| `incomplete_data` | general |
| `other` | general |

Invalid `reason_code` → HTTP 400.

## Task detail, comments, documents [#task-detail-comments-documents]

API operations:

- `GET /v1/kyc/review-tasks/{task_id}`

Unassigned tasks omit `assigned_to` / `assigned_to_type` / `assigned_display_name` (they are not defaulted to `admin_user`). After assign or decision, type is one of `admin_user` | `superadmin_user` | `api_key`.

API operations:

- `GET /v1/kyc/review-tasks/{task_id}/comments`

API operations:

- `POST /v1/kyc/review-tasks/{task_id}/comments`

Comment body field is **`content`** (JSON string). Example: `{"content":"Needs a clearer passport photo"}`. Response includes `author_id`, `author_type` (`api_key` for HMAC callers), and `author_display_name`.

API operations:

- `GET /v1/kyc/review-tasks/{task_id}/documents/{document_id}/url`

`document_id` may be a full storage key or a suffix under the session documents prefix. Path traversal (`..`, `\`) is rejected.

## Mutations: assign, priority, decide [#mutations-assign-priority-decide]

API operations:

- `POST /v1/kyc/review-tasks/{task_id}/assign`

Assign only succeeds for tasks in `pending` or `awaiting_resubmit`. The task is assigned to the **current API key** (not an admin user).

API operations:

- `PATCH /v1/kyc/review-tasks/{task_id}/priority`

API operations:

- `POST /v1/kyc/review-tasks/{task_id}/approve`

API operations:

- `POST /v1/kyc/review-tasks/{task_id}/reject`

Optional optimistic concurrency: send `if_unmodified_since` (RFC3339) with the last known `updated_at` / task timestamp when available.

## Resubmit [#resubmit]

API operations:

- `POST /v1/kyc/review-tasks/{task_id}/request-resubmit`

API operations:

- `GET /v1/kyc/review-tasks/{task_id}/resubmits`

Resubmit **reason codes** (step quality) are separate from final rejection codes:

| Code | Meaning |
| --- | --- |
| `illegible` | Text unreadable |
| `blurry` | Image blurred |
| `cropped` | Document cropped |
| `glare` | Glare |
| `wrong_document` | Wrong type |
| `expired_document` | Expired |
| `partial` | Incomplete capture |

Limits: ≤ 3 resubmits per session, 7-day TTL, one active resubmit request per session. See webhooks [Manual Review Resubmit events](/en/api/webhooks#manual-review-resubmit-events-verificationresubmit_).

## KYB [#kyb]

All operations are mirrored under `/tenant/v1/kyb/review-tasks` with `kyb.reviews:*` and `kyb.media:read`. Interactive reference: [KYB API reference](/en/kyb/api-reference#review-tasks) and [OpenAPI](/en/api/api-reference).

## What is admin-only [#what-is-admin-only]

Not exposed on the Tenant API (admin console / JWT only):

- reassign / unassign to another human reviewer
- delegate to the OneKYC manual-review team
- cancel a resubmit request
- list resubmittable steps helper endpoints used by the console UI

## Related [#related]

- [Webhooks](/en/api/webhooks) — `verification.needs_review`, resubmit events, terminal outcomes
- [Authentication](/en/api/authentication) — HMAC and scopes
- [Admin → Review](/en/admin/review) — operator UI workflow
- [OpenAPI](/en/api/api-reference) — full machine-readable spec
