> For the complete documentation index, see [llms.txt](https://finext.gitbook.io/one-kyc/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://finext.gitbook.io/one-kyc/kyb/webhooks.md).

# KYB webhooks

The KYB webhook system delivers business verification event notifications to your server in real time. The delivery mechanism mirrors KYC webhooks: HMAC-SHA256 signing, exponential backoff between retries, and idempotency support.

**Key properties:**

* 12 event types covering the full KYB verification lifecycle
* The `kyb.business.verified`, `kyb.business.rejected`, and `kyb.business.review_required` events carry the **full business data** in the `data` field (matching the `GET /v1/kyb/businesses/{id}` API response): legal form, directors, beneficial owners with UBO fields, the KYB input `profile` (`email`, `phone`, `trade_name`, `business_type`, and related profile fields), rejection reasons, last verification timestamp, and other fields
* HMAC-SHA256 signing for delivery integrity (the `X-Onekyc-Signature` header)
* Automatic retries with exponential backoff (up to 5 attempts)
* SHA-256 idempotency keys to prevent duplicate processing
* Delivery log in the **Webhooks** section of the admin panel

You configure endpoints in the **Webhooks** section of the admin panel (the same section as KYC). When creating or editing an endpoint, choose the **KYB events** category and pick the event types you need (or select "All events" to also receive KYB events).

***

## Event list

| Event                          | Description                                                              |
| ------------------------------ | ------------------------------------------------------------------------ |
| `kyb.session.created`          | KYB session created (the user opened the verification link)              |
| `kyb.session.started`          | KYB session started (the user began entering data)                       |
| `kyb.session.completed`        | KYB session completed by the user                                        |
| `kyb.session.expired`          | KYB session expired                                                      |
| `kyb.session.cancelled`        | KYB session cancelled                                                    |
| `kyb.business.verified`        | Business successfully verified                                           |
| `kyb.business.rejected`        | Business rejected                                                        |
| `kyb.business.review_required` | Business routed to manual review by an operator                          |
| `kyb.step.started`             | Verification step started (for example, sending the AI analysis request) |
| `kyb.step.completed`           | Verification step completed (for example, AI analysis)                   |
| `kyb.step.failed`              | Verification step finished with an error                                 |
| `kyb.step.skipped`             | Verification step skipped because an upstream dependency did not pass    |

***

## Payload format

Every KYB event uses a single envelope format, identical to KYC:

```json
{
  "id": "evt_uuid",
  "type": "kyb.business.verified",
  "api_version": "2026-02-01",
  "created_at": "2026-02-24T10:05:00Z",
  "tenant_id": "tenant_uuid",
  "data": { ... }
}
```

***

## Example: kyb.business.verified

The `data` field contains a **full snapshot of the business** (matching the field set returned by `GET /v1/kyb/businesses/{id}`), including the `profile` block with fields entered during the KYB flow:

```json
{
  "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": "TOO",
    "company_status": "active",
    "status_date": "2015-06-15",
    "registration_date": "2015-06-15",
    "legal_address": "Almaty, Primernaya St. 1",
    "actual_address": "Almaty, Primernaya St. 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": "Ivanov Ivan Ivanovich",
    "director_title": "CEO",
    "directors": [{ "name": "Ivanov Ivan Ivanovich", "title": "CEO" }],
    "beneficiaries": [{ "name": "Petrov P.P.", "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` field reference:**

| Field                                   | Type    | Description                                                                                                                                                        |
| --------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `business_id`                           | string  | UUID of the business in OneKYC                                                                                                                                     |
| `external_id`                           | string  | External identifier of the business in your system                                                                                                                 |
| `session_id`                            | string  | UUID of the current verification session                                                                                                                           |
| `last_session_id`                       | string  | UUID of the latest session (same as `session_id` for the first verification)                                                                                       |
| `company_name`                          | string  | Company name                                                                                                                                                       |
| `registration_number`                   | string  | Company registration number                                                                                                                                        |
| `tax_number`                            | string  | INN / BIN                                                                                                                                                          |
| `country`                               | string  | ISO country code of registration                                                                                                                                   |
| `legal_form`                            | string  | Legal form                                                                                                                                                         |
| `company_status`                        | string  | Company status in the state register (`active`, `liquidating`, `liquidated`, etc.)                                                                                 |
| `status_date`                           | string  | Date the current status was assigned                                                                                                                               |
| `status_info`                           | string  | Additional status information                                                                                                                                      |
| `liquidation_date`                      | string  | Liquidation date (when applicable)                                                                                                                                 |
| `legal_address`                         | string  | Legal address                                                                                                                                                      |
| `actual_address`                        | string  | Operating address                                                                                                                                                  |
| `main_url`                              | string  | Official website address                                                                                                                                           |
| `profile`                               | object  | Company profile fields entered during the KYB flow (`email`, `phone`, `trade_name`, `website`, `business_type`, activity fields, ownership structure, identifiers) |
| `registration_date`                     | string  | State registration date                                                                                                                                            |
| `director_name`                         | string  | Director's full name                                                                                                                                               |
| `director_title`                        | string  | Director's title                                                                                                                                                   |
| `directors`                             | array   | List of directors `[{name, title}]`                                                                                                                                |
| `beneficiaries`                         | array   | List of beneficial owners `[{name, share, type, country, pep}]`                                                                                                    |
| `activities`                            | object  | Activity types `{main, additional}`                                                                                                                                |
| `ai_confirmation`                       | boolean | AI confirmation result                                                                                                                                             |
| `ai_summary`                            | string  | Short AI analysis summary                                                                                                                                          |
| `ai_notes`                              | string  | Additional AI analysis notes                                                                                                                                       |
| `ai_sources`                            | array   | Sources used during AI analysis                                                                                                                                    |
| `director_unavailable_reason_info`      | string  | Reason director data is unavailable                                                                                                                                |
| `beneficiaries_unavailable_reason_info` | string  | Reason beneficial owner data is unavailable                                                                                                                        |
| `activities_unavailable_reason_info`    | string  | Reason activity data is unavailable                                                                                                                                |
| `main_url_unavailable_reason_info`      | string  | Reason website information is unavailable                                                                                                                          |
| `status_evidence_sources`               | array   | Sources for the company status data                                                                                                                                |
| `director_evidence_sources`             | array   | Sources for the director data                                                                                                                                      |
| `beneficiaries_evidence_sources`        | array   | Sources for the beneficial owner data                                                                                                                              |
| `activities_evidence_sources`           | array   | Sources for the activity data                                                                                                                                      |
| `main_url_evidence_sources`             | array   | Sources for the website data                                                                                                                                       |
| `risk_score`                            | number  | Risk score (0–100)                                                                                                                                                 |
| `rejection_reasons`                     | array   | Rejection reasons (for the `rejected` status)                                                                                                                      |
| `last_verified_at`                      | string  | Last verification timestamp (RFC 3339)                                                                                                                             |
| `verification_status`                   | string  | Verification status: `verified`, `rejected`, `review`, `pending`                                                                                                   |
| `business_document_key`                 | string  | Business document key in object storage                                                                                                                            |
| `metadata`                              | object  | Additional metadata                                                                                                                                                |
| `created_at`                            | string  | Record creation timestamp (RFC 3339)                                                                                                                               |
| `updated_at`                            | string  | Record update timestamp (RFC 3339)                                                                                                                                 |

> Fields with empty or zero values may be absent from the payload.

***

## Example: kyb.business.rejected

The `data` field carries the full business data, including the `rejection_reasons` array describing the rejection reasons:

```json
{
  "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"
  }
}
```

***

## Example: kyb.business.review\_required

The `data` field carries the full business data (including `directors`, `beneficiaries`, `legal_form`, and the rest):

```json
{
  "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": "AO",
    "directors": [{ "name": "Sidorov S.S.", "title": "Director" }],
    "beneficiaries": [{ "name": "OOO Holding", "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"
  }
}
```

***

## Example: kyb.session.completed

```json
{
  "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"
  }
}
```

***

## Signature verification

The mechanism is identical to KYC webhooks. Every request carries the header:

```
X-Onekyc-Signature: t=<unix_timestamp>,v1=<hex_signature>
```

During secret rotation, `v2=<new_signature>` is added. For the full description, see [KYC webhooks](/one-kyc/api/webhooks.md#verifying-the-signature).

Signature verification algorithm:

```javascript
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];

  // Verify the timestamp is fresh (≤ 5 minutes old)
  if (Math.floor(Date.now() / 1000) - parseInt(timestamp) > 300) {
    throw new Error('Webhook timestamp is too old');
  }

  // Build the signed string: 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));
}
```

***

## Retry policy

If delivery fails (a non-2xx response or a 30-second timeout), OneKYC retries with exponential backoff. The base delay is 5 seconds, doubled on each attempt, with a maximum interval of 24 hours:

| Attempt | Approximate delay                               |
| ------- | ----------------------------------------------- |
| 1       | \~5 s                                           |
| 2       | \~10 s                                          |
| 3       | \~20 s                                          |
| 4       | \~40 s                                          |
| 5       | Final attempt, then DLQ if delivery still fails |

Delays vary by ±20% (jitter). The system performs up to 5 total delivery attempts. After all attempts are exhausted, the event is moved to the dead-letter queue. You can trigger redelivery manually from the **Webhooks** section of the admin panel.
