Вебхуки KYB
Система вебхуков KYB обеспечивает доставку уведомлений о событиях верификации юридических лиц на ваш сервер в режиме реального времени. Механизм доставки аналогичен вебхукам KYC: криптографическая подпись HMAC-SHA256, экспоненциальное увеличение интервала между повторными попытками, поддержка идемпотентности.
Основные характеристики:
- 12 типов событий, охватывающих полный жизненный цикл KYB-верификации
- События
kyb.business.verified,kyb.business.rejected,kyb.business.review_requiredсодержат полный набор данных компании в полеdata(аналогично ответу APIGET /v1/kyb/businesses/{id}): организационно-правовая форма, руководители, бенефициарные владельцы с UBO-полями, KYB-анкетаprofile(email,phone,trade_name,business_typeи связанные поля), причины отклонения, дата последней верификации и прочие поля - Криптографическая подпись HMAC-SHA256 для обеспечения безопасности доставки (заголовок
X-Onekyc-Signature) - Автоматические повторные попытки с экспоненциальным увеличением интервала (до 5 попыток)
- Ключи идемпотентности на основе SHA-256 для предотвращения дублирования
- Журнал доставки в разделе Вебхуки панели администратора
Настройка endpoint осуществляется в разделе Вебхуки панели администратора (тот же раздел, что и для KYC). При создании или редактировании endpoint выберите категорию KYB events и отметьте необходимые типы событий (или «Все события» для получения, в том числе, событий KYB).
Как интерпретировать решение
- Наличие данных компании,
company_status: activeи событияkyb.step.completedне означают успешную KYB-верификацию. - Для
kyb.business.*используйтеdata.verification_status:verified,rejectedилиreview. Состояниеreviewтребует решения проверяющего и не является одобрением. - Для
kyb.session.completedиспользуйтеdata.result:approvedозначает одобрение;rejectedилиdeclined— отказ. Полеstatusописывает состояние процесса. В исторических событиях отказ может иметьstatus: failed; завершённый процесс может иметьstatus: completedпри любом финальном решении. - Поле
decision_sourceотличаетautomaticотmanual_review. Для ручного решения передаютсяdecision_idи, при наличии,decision_reason_codes. Отсутствие этих полей в старом событии не доказывает автоматическое одобрение. - Ручное одобрение не изменяет результаты отдельных проверок. Анализируйте
verifications, вложенные KYB-доказательства,beneficiary_checksиdecision_reasons, если интеграции важно знать об отсутствии реестрового подтверждения, проблемах подписи или других сигналах.ai_confirmationне заменяет итоговый статус. business_is_current_session: falseозначает историческую сессию: её одобрение не должно заменять текущее решение по компании. Отсутствие поля означает, что связь с текущей сессией не установлена. Для изменений текущего решения компании используйте событияkyb.business.*.- Вложенный
business.verification_statusв финальном событии относится к решению этой сессии; исходный результат поиска сохраняется в доказательствах. При отказе коды ручного решения передаются отдельно от технических результатов проверок.
Сохранение решения, KYB-проекции компании и обязательных событий выполняется в одной транзакции. Ошибка сохранения события возвращает ошибку операции; HTTP-доставка выполняется асинхронно через outbox. HTTP 2xx подтверждает приём запроса вашим сервером, но не проверяет бизнес-обработку на вашей стороне.
Доставка может повторяться и приходить в другом порядке из-за повторных попыток. Учитывайте ID события, ID сессии и время решения. Два подписанных endpoint получают отдельную доставку одного события; одинаковые данные не означают два решения. Новое завершение после переоткрытия сессии создаёт новое событие, даже если итоговый статус не изменился.
Перечень событий
| Событие | Описание |
|---|---|
kyb.session.created | KYB-сессия создана (пользователь перешёл по ссылке верификации) |
kyb.session.started | KYB-сессия начата (пользователь приступил к заполнению данных) |
kyb.session.completed | Получено финальное решение KYB-сессии (одобрение или отказ) |
kyb.session.expired | Срок действия KYB-сессии истёк |
kyb.session.cancelled | KYB-сессия отменена |
kyb.business.verified | Компания успешно верифицирована |
kyb.business.rejected | Компания отклонена |
kyb.business.review_required | Компания направлена на ручную проверку оператором |
kyb.step.started | Шаг верификации запущен (например, отправка запроса на ИИ-анализ) |
kyb.step.completed | Шаг верификации завершён (например, ИИ-анализ) |
kyb.step.failed | Шаг верификации завершился с ошибкой |
kyb.step.skipped | Шаг верификации пропущен, потому что вышестоящая зависимость не прошла |
Формат payload
Все события KYB используют единый формат конверта, аналогичный KYC:
{
"id": "evt_uuid",
"type": "kyb.business.verified",
"api_version": "2026-02-01",
"created_at": "2026-02-24T10:05:00Z",
"tenant_id": "tenant_uuid",
"data": { ... }
}Пример: kyb.business.verified
Поле data содержит полный снимок данных компании (идентичен набору полей в ответе API GET /v1/kyb/businesses/{id}), включая блок profile с полями, введёнными во время KYB-флоу:
{
"id": "evt_kyb_biz_001",
"type": "kyb.business.verified",
"api_version": "2026-02-01",
"created_at": "2026-02-24T10:05:00Z",
"tenant_id": "tenant_uuid",
"data": {
"business_id": "biz_uuid",
"external_id": "company-001",
"session_id": "sess_uuid",
"last_session_id": "sess_uuid",
"company_name": "Acme Corp Ltd",
"registration_number": "KZ123456789",
"tax_number": "123456789012",
"country": "KZ",
"legal_form": "ТОО",
"company_status": "active",
"status_date": "2015-06-15",
"registration_date": "2015-06-15",
"legal_address": "г. Алматы, ул. Примерная 1",
"actual_address": "г. Алматы, ул. Примерная 1",
"main_url": "https://acmecorp.kz",
"profile": {
"email": "compliance@acmecorp.kz",
"phone": "+77271234567",
"trade_name": "Acme",
"website": "https://acmecorp.kz",
"business_type": "b2b",
"business_activity": "Software development",
"ownership_structure": "Private company"
},
"director_name": "Иванов Иван Иванович",
"director_title": "Генеральный директор",
"directors": [{ "name": "Иванов Иван Иванович", "title": "Генеральный директор" }],
"beneficiaries": [{ "name": "Петров П.П.", "share": "60%", "type": "natural_person", "country": "KZ", "pep": false }],
"ai_confirmation": true,
"ai_summary": "Company has been operating since 2015, no adverse findings.",
"ai_sources": ["gosreestr.kz", "kgd.gov.kz"],
"status_evidence_sources": ["gosreestr.kz"],
"risk_score": 8.5,
"rejection_reasons": [],
"last_verified_at": "2026-02-24T10:05:00Z",
"verification_status": "verified",
"business_document_key": "business-docs/tenant_uuid/biz_uuid/charter.pdf",
"created_at": "2026-02-24T09:00:00Z",
"updated_at": "2026-02-24T10:05:00Z"
}
}Описание полей data:
| Поле | Тип | Описание |
|---|---|---|
business_id | string | UUID компании в системе OneKYC |
external_id | string | Внешний идентификатор компании в вашей системе |
session_id | string | UUID текущей сессии верификации |
last_session_id | string | UUID последней сессии (совпадает с session_id при первой верификации) |
company_name | string | Наименование компании |
registration_number | string | Регистрационный номер компании |
tax_number | string | ИНН / БИН |
country | string | ISO-код страны регистрации |
legal_form | string | Организационно-правовая форма |
company_status | string | Статус компании в государственном реестре (active, liquidating, liquidated и др.) |
status_date | string | Дата присвоения текущего статуса |
status_info | string | Дополнительные сведения о статусе |
liquidation_date | string | Дата ликвидации (при наличии) |
legal_address | string | Юридический адрес |
actual_address | string | Фактический адрес |
main_url | string | Адрес официального сайта |
profile | object | Поля анкеты компании, введённые во время KYB-флоу (email, phone, trade_name, website, business_type, данные деятельности, структура владения, идентификаторы) |
registration_date | string | Дата государственной регистрации |
director_name | string | ФИО руководителя |
director_title | string | Должность руководителя |
directors | array | Перечень руководителей [{name, title}] |
beneficiaries | array | Перечень бенефициарных владельцев [{name, share, type, country, pep}] |
activities | object | Виды деятельности {main, additional} |
ai_confirmation | boolean | Результат ИИ-подтверждения |
ai_summary | string | Краткое резюме ИИ-анализа |
ai_notes | string | Дополнительные заметки ИИ-анализа |
ai_sources | array | Источники, использованные при ИИ-анализе |
director_unavailable_reason_info | string | Основание недоступности данных о руководителе |
beneficiaries_unavailable_reason_info | string | Основание недоступности данных о бенефициарных владельцах |
activities_unavailable_reason_info | string | Основание недоступности данных о видах деятельности |
main_url_unavailable_reason_info | string | Основание недоступности сведений о сайте |
status_evidence_sources | array | Источники сведений о статусе компании |
director_evidence_sources | array | Источники сведений о руководителе |
beneficiaries_evidence_sources | array | Источники сведений о бенефициарных владельцах |
activities_evidence_sources | array | Источники сведений о видах деятельности |
main_url_evidence_sources | array | Источники сведений о сайте |
risk_score | number | Оценка риска (0–100) |
rejection_reasons | array | Причины отклонения (для статуса rejected) |
last_verified_at | string | Дата и время последней верификации (RFC 3339) |
verification_status | string | Статус верификации: verified, rejected, review, pending |
business_document_key | string | Ключ документа компании в объектном хранилище |
metadata | object | Дополнительные метаданные |
created_at | string | Дата и время создания записи (RFC 3339) |
updated_at | string | Дата и время последнего обновления записи (RFC 3339) |
Поля с пустыми или нулевыми значениями могут отсутствовать в payload.
Пример: kyb.business.rejected
Поле data содержит полные данные компании, включая массив rejection_reasons с указанием причин отклонения:
{
"id": "evt_kyb_biz_002",
"type": "kyb.business.rejected",
"api_version": "2026-02-01",
"created_at": "2026-02-24T10:08:00Z",
"tenant_id": "tenant_uuid",
"data": {
"business_id": "biz_uuid",
"external_id": "company-002",
"session_id": "sess_uuid_2",
"company_name": "Shell Corp",
"registration_number": "XX999999",
"country": "XX",
"legal_form": "LLC",
"risk_score": 72.3,
"ai_confirmation": false,
"rejection_reasons": [
"company_not_found_in_registry",
"high_risk_jurisdiction"
],
"verification_status": "rejected",
"created_at": "2026-02-24T09:00:00Z",
"updated_at": "2026-02-24T10:08:00Z"
}
}Пример: kyb.business.review_required
Поле data содержит полные данные компании (включая directors, beneficiaries, legal_form и прочие поля):
{
"id": "evt_kyb_biz_003",
"type": "kyb.business.review_required",
"api_version": "2026-02-01",
"created_at": "2026-02-24T10:10:00Z",
"tenant_id": "tenant_uuid",
"data": {
"business_id": "biz_uuid",
"external_id": "company-003",
"session_id": "sess_uuid_3",
"company_name": "Ambiguous LLC",
"registration_number": "KZ111222333",
"country": "KZ",
"legal_form": "АО",
"directors": [{ "name": "Сидоров С.С.", "title": "Директор" }],
"beneficiaries": [{ "name": "ООО Холдинг", "share": "100%", "type": "legal_entity" }],
"risk_score": 45.0,
"ai_confirmation": false,
"verification_status": "review",
"created_at": "2026-02-24T09:00:00Z",
"updated_at": "2026-02-24T10:10:00Z"
}
}Пример: kyb.session.completed
{
"id": "evt_kyb_sess_001",
"type": "kyb.session.completed",
"api_version": "2026-02-01",
"created_at": "2026-02-24T10:04:00Z",
"tenant_id": "tenant_uuid",
"data": {
"session_id": "sess_uuid",
"flow_id": "flow_uuid",
"external_id": "company-001",
"status": "completed",
"scope": "kyb",
"result": "approved",
"business_id": "biz_uuid",
"completed_at": "2026-02-24T10:04:00Z"
}
}Верификация подписи
Механизм идентичен вебхукам KYC. Каждый запрос содержит заголовок:
X-Onekyc-Signature: t=<unix_timestamp>,v1=<hex_подпись>При ротации секретного ключа дополнительно добавляется v2=<новая_подпись>. Подробное описание приведено в разделе Вебхуки KYC.
Алгоритм проверки подписи:
const crypto = require('crypto');
function verifyWebhook(rawBody, signatureHeader, secret) {
const parts = signatureHeader.split(',');
const timestamp = parts.find(p => p.startsWith('t=')).split('=')[1];
const v1Signature = parts.find(p => p.startsWith('v1=')).split('=')[1];
// Проверка актуальности timestamp (не старше 5 минут)
if (Math.floor(Date.now() / 1000) - parseInt(timestamp) > 300) {
throw new Error('Webhook timestamp is too old');
}
// Формирование строки для подписи: timestamp.raw_body
const signedPayload = `${timestamp}.${rawBody}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1Signature));
}Политика повторных попыток
При неуспешной доставке (ответ с кодом, отличным от 2xx, или превышение времени ожидания 30 секунд) система OneKYC выполняет повторную отправку с экспоненциальным увеличением интервала. Базовая задержка — 5 секунд, удваивается на каждой попытке, максимальный интервал — 24 часа:
| Попытка | Ориентировочная задержка |
|---|---|
| 1 | ~5 с |
| 2 | ~10 с |
| 3 | ~20 с |
| 4 | ~40 с |
| 5 | Финальная попытка, затем DLQ, если доставка всё ещё неуспешна |
Задержки варьируются в пределах +/-20 % (jitter). Всего система выполняет до 5 общих попыток доставки. После исчерпания всех попыток событие помещается в очередь недоставленных сообщений (Dead Letter Queue). Повторную отправку можно инициировать вручную из раздела Вебхуки панели администратора.
Итоговые данные о компании
kyb.session.completed включает доступные business_id, external_id, company_name верхнего уровня и профиль business как для автоматических решений, так и для решений ревьюера. Профиль содержит доступные данные о регистрации, налогах, юрисдикции, адресе, организационно-правовой форме, директорах/бенефициарах и результаты поиска. Используются данные, собранные в этой сессии, или связанная с ней сохранённая компания; профиль компании из другой сессии не подставляется.
Включаются доступные актуальные верификации, анализ документов, загруженные media_files, причины решения, пользовательские шаги, сводки по рискам и устройствам, а также KYC-проверки бенефициаров. Результаты поиска сохраняют исход, причину, охват, использованные источники и следующие действия. Необработанные данные провайдеров, внутренние ссылки и пути хранения из анализа документов исключены.
Для физических лиц компании поля is_pep и is_sanctioned необязательны. false означает зафиксированный отрицательный результат; отсутствующее поле означает неизвестно или не проверялось. Не приводите отсутствие поля к false.
Вебхуки комментариев ревью
Новый комментарий к задаче 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:
{
"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-доставка выполняется асинхронно после фиксации транзакции: успешный ответ создания не подтверждает доставку вебхука клиенту; проверяйте журнал доставок.