Finance OS / API

KYC Verification

KYC-as-a-Service для managed customers: партнёр от имени своего end-user'а запускает верификацию, загружает документы, подтверждает телефон по SMS и отслеживает статус. Finance OS выступает прокси-фасадом — документы принимаются на стороне Finance OS и передаются во внутреннюю систему верификации; апстрим-инфраструктура партнёру не видна.

Предусловие: consent
Consent end-user'а (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.
Окружение выводится из ключа
Sandbox или live определяется префиксом ключа, а не заголовком: 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.

платежи.'], ['name' => 'rejected', 'type' => 'терминальный', 'desc' => 'Проверка отклонена (live). Причина — в kyc_rejection_reason. Начать заново можно через reset.'], ['name' => 'expired', 'type' => 'терминальный', 'desc' => 'Сессия / результат верификации истёк (live). Требуется новый start после reset.'], ]" />
Sandbox magic: код 000000
В sandbox апстрим не вызывается. 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

POST /api/v1/customers/{uuid}/kyc/start
Bearer Token

Запустить KYC-сессию

Создаёт verification-запись для customer'а и переводит 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

POST /api/v1/customers/{uuid}/kyc/documents
Bearer Token

Загрузить документы (multipart/form-data)

Принимает до трёх файлов. Все поля опциональны — можно догружать по одному. В sandbox загрузка паспорта/адреса переводит их состояние в 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_uploadeduploadedsuccess (или rejected в live).

3. Request code

POST /api/v1/customers/{uuid}/kyc/request-code
Bearer Token

Отправить 6-значный SMS-код

Тело не требуется. В live код отправляется на телефон из 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

POST /api/v1/customers/{uuid}/kyc/verify-code
Bearer Token

Подтвердить код и завершить верификацию

Завершает flow. В sandbox код 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

GET /api/v1/customers/{uuid}/kyc/status
Bearer Token

Снимок текущего состояния KYC

Читается без consent — используйте для поллинга. Возвращает статус, уровень, причину отклонения, флаг consent, состояние документов, телефон, окружение и остаток попыток 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

POST /api/v1/customers/{uuid}/kyc/reset
Bearer Token

Сбросить и начать заново

Обнуляет документы и состояние: kyc_statusnot_started, kyc_level0. Лимит — 5 сбросов за 24 часа на customer'а; при превышении 422 RESET_LIMIT_EXCEEDED. После сброса начните с start.

Responses

{
  "data": {
    "ok": true,
    "attempts_remaining": 4
  }
}

Коды ошибок

Ошибки возвращаются в конверте { "error": { "code", "message", "request_id" } }.

запишите consent.'], ['name' => 'KYC_SESSION_MISSING', 'type' => '422', 'desc' => '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-режим не включён на учётке владельца ключа.'], ]" />