К содержимому
OneKYC
KYB API

Вебхуки KYB

Обновлено:

Система вебхуков KYB обеспечивает доставку уведомлений о событиях верификации юридических лиц на ваш сервер в режиме реального времени. Механизм доставки аналогичен вебхукам KYC: криптографическая подпись HMAC-SHA256, экспоненциальное увеличение интервала между повторными попытками, поддержка идемпотентности.

Основные характеристики:

  • 12 типов событий, охватывающих полный жизненный цикл KYB-верификации
  • События kyb.business.verified, kyb.business.rejected, kyb.business.review_required содержат полный набор данных компании в поле data (аналогично ответу API GET /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.createdKYB-сессия создана (пользователь перешёл по ссылке верификации)
kyb.session.startedKYB-сессия начата (пользователь приступил к заполнению данных)
kyb.session.completedПолучено финальное решение KYB-сессии (одобрение или отказ)
kyb.session.expiredСрок действия KYB-сессии истёк
kyb.session.cancelledKYB-сессия отменена
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_idstringUUID компании в системе OneKYC
external_idstringВнешний идентификатор компании в вашей системе
session_idstringUUID текущей сессии верификации
last_session_idstringUUID последней сессии (совпадает с session_id при первой верификации)
company_namestringНаименование компании
registration_numberstringРегистрационный номер компании
tax_numberstringИНН / БИН
countrystringISO-код страны регистрации
legal_formstringОрганизационно-правовая форма
company_statusstringСтатус компании в государственном реестре (active, liquidating, liquidated и др.)
status_datestringДата присвоения текущего статуса
status_infostringДополнительные сведения о статусе
liquidation_datestringДата ликвидации (при наличии)
legal_addressstringЮридический адрес
actual_addressstringФактический адрес
main_urlstringАдрес официального сайта
profileobjectПоля анкеты компании, введённые во время KYB-флоу (email, phone, trade_name, website, business_type, данные деятельности, структура владения, идентификаторы)
registration_datestringДата государственной регистрации
director_namestringФИО руководителя
director_titlestringДолжность руководителя
directorsarrayПеречень руководителей [{name, title}]
beneficiariesarrayПеречень бенефициарных владельцев [{name, share, type, country, pep}]
activitiesobjectВиды деятельности {main, additional}
ai_confirmationbooleanРезультат ИИ-подтверждения
ai_summarystringКраткое резюме ИИ-анализа
ai_notesstringДополнительные заметки ИИ-анализа
ai_sourcesarrayИсточники, использованные при ИИ-анализе
director_unavailable_reason_infostringОснование недоступности данных о руководителе
beneficiaries_unavailable_reason_infostringОснование недоступности данных о бенефициарных владельцах
activities_unavailable_reason_infostringОснование недоступности данных о видах деятельности
main_url_unavailable_reason_infostringОснование недоступности сведений о сайте
status_evidence_sourcesarrayИсточники сведений о статусе компании
director_evidence_sourcesarrayИсточники сведений о руководителе
beneficiaries_evidence_sourcesarrayИсточники сведений о бенефициарных владельцах
activities_evidence_sourcesarrayИсточники сведений о видах деятельности
main_url_evidence_sourcesarrayИсточники сведений о сайте
risk_scorenumberОценка риска (0–100)
rejection_reasonsarrayПричины отклонения (для статуса rejected)
last_verified_atstringДата и время последней верификации (RFC 3339)
verification_statusstringСтатус верификации: verified, rejected, review, pending
business_document_keystringКлюч документа компании в объектном хранилище
metadataobjectДополнительные метаданные
created_atstringДата и время создания записи (RFC 3339)
updated_atstringДата и время последнего обновления записи (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-доставка выполняется асинхронно после фиксации транзакции: успешный ответ создания не подтверждает доставку вебхука клиенту; проверяйте журнал доставок.

На этой странице