KYC Verification
KYC-as-a-Service для managed customers: партнёр от имени своего end-user'а запускает верификацию, загружает документы, подтверждает телефон по SMS и отслеживает статус. Finance OS выступает прокси-фасадом — документы принимаются на стороне Finance OS и передаются во внутреннюю систему верификации; апстрим-инфраструктура партнёру не видна.
consent_signed=true) должен быть зафиксирован до любой KYC-операции. Иначе start, documents, request-code, verify-code и reset вернут 422 CONSENT_REQUIRED. Сначала вызовите POST /api/v1/customers/{uuid}/consent. Endpoint GET /kyc/status — единственный, который читается без consent.
sk_test_ → sandbox, sk_live_ → live. Ключ и env customer'а должны совпадать, иначе 404. Все endpoints требуют Authorization: Bearer и включённого B2B-режима (иначе 403 B2B_ACCESS_REQUIRED).
Машина состояний kyc_status
У customer'а шесть возможных статусов. В sandbox переходы детерминированы и управляются вашими вызовами; в live финальные статусы (verified / rejected / expired) приходят асинхронно и синхронизируются через webhook.
kyc_rejection_reason. Начать заново можно через reset.'],
['name' => 'expired', 'type' => 'терминальный', 'desc' => 'Сессия / результат верификации истёк (live). Требуется новый start после reset.'],
]" />
request-code возвращает mock_code="000000", а verify-code с кодом 000000 мгновенно переводит customer'а в verified с kyc_level=2. Любой другой код → 422 INVALID_CODE.
Уровни kyc_level
Числовой уровень доверия, ортогональный статусу. Растёт по мере прохождения этапов; при успешной верификации физлица присваивается уровень 2 (в sandbox — всегда 2).
Уровни
| Name | Type | Required | Description |
|---|---|---|---|
0
|
numeric
|
optional |
Не верифицирован. Значение по умолчанию и после reset.
|
1
|
numeric
|
optional | Базовый: телефон привязан, документы ещё не подтверждены. |
2
|
numeric
|
optional |
Полная верификация физлица: документ + селфи + подтверждённый телефон. Присваивается при успешном verify-code.
|
3
|
numeric
|
optional | Расширенная проверка (подтверждение адреса / дополнительные документы). |
1. Start
/api/v1/customers/{uuid}/kyc/start
Запустить KYC-сессию
kyc_status из not_started в pending. В live дополнительно регистрирует customer'а во внутренней системе верификации; в sandbox апстрим не вызывается.Body
| Name | Type | Required | Description |
|---|---|---|---|
phone
|
string
|
required |
Телефон end-user'а в формате E.164 (+ и 7–20 цифр, regex ^\+[1-9][0-9]{6,19}$). Пример: +79001234567.
|
passport_type
|
enum
|
optional |
ru (внутренний паспорт РФ) или foreign (иностранный документ).
Default:
ru |
Responses
{
"data": {
"session_id": "sbx_0f3ab9c7d21e4a6f8b1c2d3e",
"next": "upload_documents",
"env": "sandbox",
"customer_uuid": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"kyc_status": "pending"
}
}
2. Documents
/api/v1/customers/{uuid}/kyc/documents
Загрузить документы (multipart/form-data)
uploaded и двигает kyc_status в processing.Form-data (файлы)
| Name | Type | Required | Description |
|---|---|---|---|
selfie
|
file
|
optional |
Селфи держателя документа. jpg, jpeg, png или pdf, до 10 MB.
|
passport
|
file
|
optional |
Разворот удостоверения личности. jpg, jpeg, png или pdf, до 10 MB.
|
address
|
file
|
optional |
Подтверждение адреса (опционально, для уровня 3). jpg, jpeg, png или pdf, до 10 MB.
|
Responses
{
"data": {
"customer_uuid": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"passport_state": "uploaded",
"address_state": "not_uploaded",
"kyc_status": "processing"
}
}
Состояния документа (passport_state, address_state): not_uploaded → uploaded → success (или rejected в live).
3. Request code
/api/v1/customers/{uuid}/kyc/request-code
Отправить 6-значный SMS-код
start (в ответе — только метаданные доставки, без самого кода). В sandbox SMS не отправляется — возвращается мок-код 000000. Статус kyc_status этот вызов не меняет.Responses
{
"data": {
"sent": true,
"channel": "sandbox",
"mock_code": "000000",
"expires_in": 300,
"note": "In sandbox the code \"000000\" always succeeds."
}
}
4. Verify code
/api/v1/customers/{uuid}/kyc/verify-code
Подтвердить код и завершить верификацию
000000 переводит customer'а в verified (kyc_level=2); любой другой код → 422 INVALID_CODE. В live результат синхронизируется с внутренней системой верификации.Body
| Name | Type | Required | Description |
|---|---|---|---|
code
|
string
|
required |
Ровно 6 символов. В sandbox всегда 000000.
|
Responses
{
"data": {
"customer_uuid": "1a7ee75b-7f0e-48af-a075-a56721f1140e",
"kyc_status": "verified",
"kyc_level": 2
}
}
{
"error": {
"code": "INVALID_CODE",
"message": "Invalid code. In sandbox the code \"000000\" always succeeds.",
"request_id": "req_9f2ac1b0e7d8"
}
}
5. Status
/api/v1/customers/{uuid}/kyc/status
Снимок текущего состояния KYC
reset.Responses
{
"data": {
"kyc_status": "verified",
"kyc_level": 2,
"kyc_rejection_reason": null,
"consent_signed": true,
"documents": {
"selfie_uploaded": true,
"passport_state": "success",
"address_state": "success"
},
"phone": "+79001234567",
"env": "sandbox",
"attempts_remaining": 5
}
}
6. Reset
/api/v1/customers/{uuid}/kyc/reset
Сбросить и начать заново
kyc_status → not_started, kyc_level → 0. Лимит — 5 сбросов за 24 часа на customer'а; при превышении 422 RESET_LIMIT_EXCEEDED. После сброса начните с start.Responses
{
"data": {
"ok": true,
"attempts_remaining": 4
}
}
Коды ошибок
Ошибки возвращаются в конверте { "error": { "code", "message", "request_id" } }.
documents/request-code/verify-code вызваны до kyc/start.'],
['name' => 'VALIDATION_ERROR', 'type' => '422', 'desc' => 'Некорректный phone (не E.164), passport_type вне ru|foreign, файл не того типа/размера или code ≠ 6 символов.'],
['name' => 'INVALID_CODE', 'type' => '422', 'desc' => 'Неверный SMS-код (в sandbox — любой кроме 000000).'],
['name' => 'RESET_LIMIT_EXCEEDED', 'type' => '422', 'desc' => 'Превышен лимит 5 сбросов за 24 часа.'],
['name' => 'PROVIDER_UNAVAILABLE', 'type' => '503', 'desc' => 'Внутренняя система верификации временно недоступна (live). Повторите позже.'],
['name' => 'CUSTOMER_NOT_FOUND', 'type' => '404', 'desc' => 'Customer не найден или окружение ключа не совпадает с env customer\'а.'],
['name' => 'B2B_ACCESS_REQUIRED', 'type' => '403', 'desc' => 'B2B-режим не включён на учётке владельца ключа.'],
]" />