Webhook overview
OSL Pay uses a unified webhook to asynchronously notify merchants of important updates. When a user account status or order status changes, OSL Pay will call the webhook URL provided by the merchant and push the current status information of the user or order to the merchant’s system.
Webhook signature verification
When OSL Pay sends a webhook, the request headers follow the same format as OpenAPI, including appId, timestamp, and signature. You can verify the signature using the public key provided to you when integrating with OSL Pay.
| Request parameters / Request body (header) | |||
|---|---|---|---|
| Parameter | Type | Required | Description |
| appId | String | Yes | The appId assigned to the merchant when integrating with OSL Pay |
| timestamp | Long | Yes | Timestamp of the request in milliseconds |
| signature | String | Yes | The signature of the request. Example body for signing: "appId=me114702259781634×tamp=1756802303227". Use the public key provided by OSL Pay to verify the signature. See encryption & signature verification guide for sample code. |
Webhook common parameters
All Webhooks are pushed using the following standard structure. The content of the data field varies depending on the scenario.
| Webhook body | |||
|---|---|---|---|
| Parameter | Type | Required | Description |
| type | String | Yes | Webhook typekyc_status_change: Asynchronous notification of a user KYC/account opening resultdefi_account_bind_status: Notification of DEFI-type merchant account binding resultdefi_account_auth_status: Notification of DEFI-type merchant authentication resultorder_status_change: Notification of order status change |
| data | object | Yes | Detailed data |
User account opening information push (kyc_status_change)
When a merchant uses Sumsub Token Share for account opening, a Webhook is sent whenever the account status changes. The data field in the Webhook will follow the format below:
Data parameter | |||
|---|---|---|---|
Parameter | Type | Required | Description |
merchantUserId | String | No | User ID on the merchant side |
userId | String | Yes | User ID on the OSL Pay side |
kycShareFailedReason | String | No | Reason for failure (KYC failed reason) |
merchantCode | String | Yes | Merchant code |
kycStatus | String | Yes | KYC status:
|
previousUserId | Long | No | Returned when the KYC request is a duplicate Returns the OSL Pay user ID that was previously verified |
previousMerchantUserId | String | No | Returned when the KYC request is a duplicate Returns the merchant-side user ID that was previously verified |
rejectedCode | String | No | Rejection code, present when kycStatus is REJECT or FINAL_REJECT. SUMSUB_TOKEN_INVALID – Provided sumsubtoken cannot be imported to SumSub APPLICANT_STATUS_INVALID – User data in SumSub is invalid after import REJECTED_BY_SUMSUB – SumSub rejected, see rejectReason for details ID_ALREADY_EXIST – User already has a verified OSL Pay account (e.g., User B attempts to register with ID already used by User A; corresponding IDs returned in previousUserId and previousMerchantUserId) COUNTRY_FROM_SUMSUB_FORBIDDEN – OSL Pay does not support user’s country (matches any of ID country, residence country, or nationality) USER_IN_BLACK_LIST – User is on OSL Pay blacklist and cannot be verified |
questionnaireStatus | String | No | Returned when kycStatus is WAIT_QUESTIONNAIRE. • WAIT_QUESTIONNAIRE – Questionnaire pending • REVIEWING – Questionnaire under review • REJECT – Questionnaire temporarily rejected, user must resubmit |
questionnaireToken | String | No | Token for interacting with OSL’s questionnaire SDK |
Request body example:
{
"data": "{\"merchantCode\":\"MER10000021\",\"kycStatus\":\"REJECT\",\"kycShareFailedReason\":\"user already exists\",\"merchantUserId\":\"88813320250090207\",\"userId\":\"100824841806683\"}",
"id": "f17d8acc92c44040b2309939e797eb8c",
"type": "kyc_status_change",
"version": "1.0.0"
}Success response example
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | String | Yes | Response code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.) |
| msg | String | Yes | Response message/description |
{
"code": "00000",
"msg": "success"
}Web3 user account opening (defi_account_bind_status)
When a Web3-type user account status changes, a Webhook is sent. The data field in the Webhook will follow the format below:
| Data parameter | |||
|---|---|---|---|
| Parameter | Type | Required | Description |
| merchantUserId | String | No | User ID on the merchant side |
| userId | String | Yes | User ID on OSL Pay side |
| String | No | User email. Only pushed when type = defi_account_bind_statusRefer to decryption guide for details | |
| kycShareFailedReason | String | No | Failure reason (KYC Failed Reason) |
| merchantCode | String | Yes | Merchant code |
| kycStatus | String | Yes | KYC status: PASS – passedREJECT – rejected |
Request body example:
{
"data": "{\"merchantCode\":\"MER10000021\",\"email\":\"XXX\",\"kycStatus\":\"REJECT\",\"kycShareFailedReason\":\"user already exists\",\"merchantUserId\":\"88813320250090207\",\"userId\":\"100824841806683\"}",
"id": "f17d8acc92c44040b2309939e797eb8c",
"type": "defi_account_bind_status",
"version": "1.0.0"
}Success response example
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | String | Yes | Response code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.) |
| msg | String | Yes | Response message/description |
{
"code": "00000",
"msg": "success"
}DEFI user authentication (defi_account_auth_status)
When a DEFI user authentication status changes, a Webhook is sent. The data field in the Webhook will follow the format below:
| Data parameter | |||
|---|---|---|---|
| Parameter | Type | Required | Description |
| reqNo | String | Yes | Serial number of the authentication request |
| checkStatus | String | Yes | Authentication status: FAIL – failed, SUCCESS – successful |
Request body example:
{
"data": "{\"reqNo\":\"REQ1000001\",\"checkStatus\":\"FAIL\"}",
"id": "f17d8acc92c44040b2309939e797eb8c",
"type": "defi_account_auth_status",
"version": "1.0.0"
}Success response example
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | String | Yes | Response code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.) |
| msg | String | Yes | Response message/description |
{
"code": "00000",
"msg": "success"
}Order status change push (order_status_change)
当When a merchant subscribes to order status changes, a Webhook is sent whenever an order status changes. The data field in the Webhook will follow the format below:
Data parameter | |||
|---|---|---|---|
Parameter | Type | Required | Description |
orderId | String | Yes | OSL Pay order ID |
merchantOrder | String | No | Merchant-side order ID |
userId | String | Yes | OSL Pay user ID |
fiatCurrency | String | Yes | Fiat currency |
cryptoCurrency | String | Yes | Cryptocurrency |
fiatAmount | String | Yes | Fiat amount |
cryptoAmount | String | Yes | Cryptocurrency amount |
orderFee | String | No | Fee |
price | String | No | Cryptocurrency price |
payWayCode | String | Yes | Payment method CARD GOOGLEPAY APPLE PAY |
state | String | Yes | Order status CREATEED (created), PROCESSING (3DS link generated), CONVERSION_FAILED (hedge failed), CHAIN_WITHDRAW_WAIT (on-chain transfer), SUCCESSED (fiat paid successfully, awaiting transfer), COMPLETED (order completed), FAILED (order failed) |
subState | String | No | Order sub-status (only when state = COMPLETED): WITHDRAWAL_SUCCESS (transfer successful), WITHDRAWAL_FAILED (transfer failed) |
errorCode | String | No | Error code |
errorMessage | String | No | Error message |
completedTime | Date | No | Order completion time |
createTime | Date | Yes | Order creation time |
updateTime | Date | Yes | Order update time |
network | String | Yes | Blockchain network |
tag | String | No | Blockchain tag |
address | String | Yes | Withdrawal address |
txId | String | No | On-chain transaction ID |
networkFee | String | No | On-chain gas fee |
chainArrivalTime | Date | No | On-chain arrival time |
orderCallbackUrl | String | Yes | Merchant Webhook callback URL |
merchantUser | String | No | Merchant-side user ID |
threeDsRedirectUrl | String | No | 3DS redirect URL |
actualReceivedAmount | String | Yes | Actual received amount |
qrcodeUrl | String | No | QR code payment link, returned when payWayCode = QRCODE |
Request body example:
{
"data": {
"orderId": "202509040001",
"merchantOrder": "MCH20250904001",
"userId": "U123456789",
"fiatCurrency": "USD",
"cryptoCurrency": "USDT",
"fiatAmount": "100.00",
"cryptoAmount": "99.50",
"orderFee": "0.50",
"price": "1.00",
"payWayCode": "VISA",
"state": "COMPLETED",
"subState": "WITHDRAWAL_FAILED",
"errorCode": "",
"errorMessage": "",
"completedTime": "2025-09-04T16:15:30Z",
"createTime": "2025-09-04T16:10:00Z",
"updateTime": "2025-09-04T16:15:30Z",
"network": "ERC20",
"tag": "",
"address": "0x9fBDa871d559710256a2502A2517b794B482Db40",
"txId": "0xabc123def4567890abcdef1234567890abcdef1234567890abcdef1234567890",
"networkFee": "5.00",
"chainArrivalTime": "2025-09-04T16:16:00Z",
"orderCallbackUrl": "https://merchant.com/callback/order",
"privateSecret": "************",
"merchantCallbackUrl": "https://merchant.com/callback",
"appid": "MCH_APP_1001",
"merchantUser": "merchantUser001",
"threeDsRedirectUrl": "https://paymentgateway.com/3ds/redirect?sessionId=abc123",
"actualReceivedAmount": "99.50"
"id": "1756974300000",
"type": "order_status_change",
"version": "1.0.0"
}Success response example
| Parameter | Type | Required | Description |
|---|---|---|---|
| code | String | Yes | Response code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.) |
| msg | String | Yes | Response message/description |
{
"code": "00000",
"msg": "success"
}Updated about 1 month ago