Sub-Account Operation — This webhook fires on KYB review outcomes for your managed sub-accounts (
business_managed). It does not apply to your own merchant-level KYB, or to owned/personal_managed sub-accounts (which never require KYB).Information
POST request to your configured kyb_notif_url whenever a managed sub-account’s KYB submission finishes review — either approved, or rejected at one of the review stages.
Events
This single webhook covers two events, distinguished by theevent field:
KYB review runs through three sequential stages: Document Check → DTTOT Check → Blacklist Check.
kyb.rejected can fire at any of the three — you will receive a separate webhook call for each rejection, not just one at the end. kyb.approved fires only once, after the final stage passes.Request Details
Headers
The
Authorization token for KYB webhooks is a randomly generated string — not a user access token. KYB review outcomes are triggered by an internal reviewer action, not a merchant request. Extract the token as-is and use it in the string to sign. See Security and signature validation.Body Parameters — common envelope
Every KYB webhook shares the same top-level envelope:number
required
HTTP Status Code. Always
200.boolean
required
Indicates a successful event. Always
true.string
required
Event type identifier. One of
"kyb.approved", "kyb.rejected".string
required
Event timestamp in format
"d M Y H:i:s". Example: "28 Aug 2026 14:33:55"object
required
Container for the account details (and, for rejections, the rejection details).
data.account (present on all events)
object
required
The sub-account the event relates to. Shape is identical to the
Account object returned by GET /accounts/{id}.data.verified_at (kyb.approved only)
string
required
Timestamp the final review stage was approved. Format
"d M Y H:i:s".data.stage, data.reason, data.rejected_at (kyb.rejected only)
string
required
Which review stage rejected the submission. One of
"document", "dttot", "blacklist".string
required
Free-text reason written by the reviewer. Not a structured error code — read it directly to determine what needs correcting.
string
required
Timestamp the rejection was recorded. Format
"d M Y H:i:s".Payload Examples
kyb.approved
kyb.rejected
Security and responses
Return HTTP200 promptly after validating the request. For retry behavior, see Webhook retry mechanism.
Verify every webhook using Security and signature validation. Use your configured kyb_notif_url path when building StringToSign.
Handle duplicate deliveries idempotently using data.account.id combined with the event (and, for rejections, data.stage) as a stable identifier.
Sub-Account KYB Specific Notes
Dedicated callback URL
Dedicated callback URL
kyb_notif_url is a dedicated field — it does not share a callback URL with any other webhook type. Configure it separately in your dashboard under Settings → Credentials → Webhook & Notification URLs.Multiple rejections per sub-account
Multiple rejections per sub-account
Review runs through three sequential stages. If a sub-merchant is rejected, corrects the issue, and resubmits, and is rejected again at a later stage, you will receive a new
kyb.rejected webhook for that stage — each rejection in the account’s history triggers its own call.The onboarding link survives rejection
The onboarding link survives rejection
kyb_onboarding_url stays the same across reject → resubmit cycles — no new link is generated on rejection. The sub-merchant reopens the same URL to fix and resubmit their data. The link is only revoked (becomes null) once kyb_status reaches kyb_verified.kyb_status alone doesn't tell you why
kyb_status alone doesn't tell you why
kyb_status = "kyb_rejected" tells you a rejection is outstanding, but the specific reason lives in kyb_rejection_reason — a free-text string written by a human reviewer, not a structured per-field error code. If you need to know exactly which document or field was flagged, parse the reason text or direct your sub-merchant to read it carefully before resubmitting.legal_name and brand_name are null until verified
legal_name and brand_name are null until verified
Both fields only populate once
kyb_status = "kyb_verified" — they are null throughout kyb_waiting_document, kyb_in_review, and kyb_rejected, including in the kyb.rejected payload itself.Timestamp format & timezone
Timestamp format & timezone
All timestamps (
timestamp, verified_at, rejected_at) use the format "d M Y H:i:s" (e.g. "28 Aug 2026 14:33:55") in Asia/Jakarta (WIB) timezone — not UTC.