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: PROCESS – In process REJECT – Rejected PASS – Approved REVIEW – Verification in progress |
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 | This field only appears when kycStatus is REJECT. Possible values: SYSTEM_ERROR – There was a system issue or timeout while processing your KYC. SUMSUB_TOKEN_INVALID – Your session token has expired or is not authorized. APPLICANT_STATUS_INVALID – There’s a mismatch in KYC requirements (for example, Sumsub requires both sides of the ID but the user only submitted one side), or missing data in the application. REJECTED_BY_SUMSUB – Your KYC was rejected by Sumsub. Please check the specific reason in kycShareFailedReason. ID_ALREADY_EXIST – An account with this ID is already verified in OSLPAY. COUNTRY_FROM_SUMSUB_FORBIDDEN – OSLPAY does not support users from this country. USER_IN_BLACK_LIST – Your account is on OSLPAY’s blacklist and cannot be verified. |
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