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)
ParameterTypeRequiredDescription
appIdStringYesThe appId assigned to the merchant when integrating with OSL Pay
timestampLongYesTimestamp of the request in milliseconds
signatureStringYesThe signature of the request. Example body for signing: "appId=me114702259781634&timestamp=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
ParameterTypeRequiredDescription
typeStringYesWebhook 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
dataobjectYesDetailed 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
ParameterTypeRequiredDescription
merchantUserIdStringNoUser ID on the merchant side
userIdStringYesUser ID on the OSL Pay side
kycShareFailedReasonStringNoReason for failure (KYC failed reason)
merchantCodeStringYesMerchant code
kycStatusStringYesKYC status: PASS for approvedREJECT for rejected
previousUserIdLongNoIf KYC is a duplicate, this is the previously bound OSL Pay user ID
previousMerchantUserIdStringNoIf KYC is a duplicate, this is the previously bound merchant-side user ID

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

ParameterTypeRequiredDescription
codeStringYesResponse code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.)
msgStringYesResponse 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
ParameterTypeRequiredDescription
merchantUserIdStringNoUser ID on the merchant side
userIdStringYesUser ID on OSL Pay side
emailStringNoUser email. Only pushed when type = defi_account_bind_statusRefer to decryption guide for details
kycShareFailedReasonStringNoFailure reason (KYC Failed Reason)
merchantCodeStringYesMerchant code
kycStatusStringYesKYC 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

ParameterTypeRequiredDescription
codeStringYesResponse code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.)
msgStringYesResponse 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
ParameterTypeRequiredDescription
reqNoStringYesSerial number of the authentication request
checkStatusStringYesAuthentication 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

ParameterTypeRequiredDescription
codeStringYesResponse code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.)
msgStringYesResponse 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

ParameterTypeRequiredDescription
codeStringYesResponse code. "00000" indicates success. (You must return this code to OSL Pay; otherwise, the Webhook will be retried up to 5 times.)
msgStringYesResponse message/description
{
    "code": "00000",
    "msg": "success"
}