Webhook overview

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×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
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:
  • PROCESS – in progress
  • REJECT – temporary rejection
  • FINAL_REJECT – final rejection
  • PASS – KYC passed
  • REVIEW – user is under OSL manual review (token-share only)
  • WAIT_QUESTIONNAIRE – Awaiting questionnaire completion
previousUserIdLongNoReturned when the KYC request is a duplicate
Returns the OSL Pay user ID that was previously verified
previousMerchantUserIdStringNoReturned when the KYC request is a duplicate
Returns the merchant-side user ID that was previously verified
rejectedCodeStringNoRejection 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
questionnaireStatusStringNoReturned when kycStatus is WAIT_QUESTIONNAIRE.
• WAIT_QUESTIONNAIRE – Questionnaire pending
• REVIEWING – Questionnaire under review
• REJECT – Questionnaire temporarily rejected, user must resubmit
questionnaireTokenStringNoToken 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

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
ParameterTypeRequiredDescription
orderIdStringYesOSL Pay order ID
merchantOrderStringNoMerchant-side order ID
userIdStringYesOSL Pay user ID
fiatCurrencyStringYesFiat currency
cryptoCurrencyStringYesCryptocurrency
fiatAmountStringYesFiat amount
cryptoAmountStringYesCryptocurrency amount
orderFeeStringNoFee
priceStringNoCryptocurrency price
payWayCodeStringYesPayment method
CARD
GOOGLEPAY
APPLE PAY
stateStringYesOrder 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)
subStateStringNoOrder sub-status (only when state = COMPLETED): WITHDRAWAL_SUCCESS (transfer successful), WITHDRAWAL_FAILED (transfer failed)
errorCodeStringNoError code
errorMessageStringNoError message
completedTimeDateNoOrder completion time
createTimeDateYesOrder creation time
updateTimeDateYesOrder update time
networkStringYesBlockchain network
tagStringNoBlockchain tag
addressStringYesWithdrawal address
txIdStringNoOn-chain transaction ID
networkFeeStringNoOn-chain gas fee
chainArrivalTimeDateNoOn-chain arrival time
orderCallbackUrlStringYesMerchant Webhook callback URL
merchantUserStringNoMerchant-side user ID
threeDsRedirectUrlStringNo3DS redirect URL
actualReceivedAmountStringYesActual received amount
qrcodeUrlStringNoQR code payment link, returned when payWayCode = QRCODE
bankTransferPaymentObjectNobankTransfer related payment infomation

bankTransferPayment object properties

ParameterTypeRequiredDescription
paymentRedirectUrlStringNoPayment redirect link. Merchants can choose to redirect to the oslPay page for payment, or use the payment information provided in the url, barcode, or va fields.
payTypeStringNoThe type of payment method. Examples: URL, BARCODE, VA (Virtual Account).
urlObjectNoPayment information when payType = URL
barcodeObjectNoPayment information when payType = BARCODE
vaObjectNoPayment information when payType = VA (Virtual Account)
amountStringYesThe payment amount in fiat currency
expiredTimeStringYesPayment expiration time, in seconds (s)

url object properties

ParameterTypeRequiredDescription
payUrlStringNoPayment link

barCode object properties

ParameterTypeRequiredDescription
barCodeStringNoPayment QR code

va object properties

ParameterTypeRequiredDescription
ibanStringNoInternational Bank Account Number (IBAN)
accountNameStringNoAccount holder name
swiftCodeStringNoSWIFT/BIC code (Bank Identifier Code)
bankNameStringNoBank name
bankAddressStringNoBank address


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": "VIETQR",
        "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",
        "bankTransferPayment": {
            "amount": "200000.00",
            "barcode": {
                "barCode": "https://osspay.s3.ap-southeast-1.amazonaws.com/qrcode247_g1778755284475.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260514T104124Z&X-Amz-SignedHeaders=host&X-Amz-Expires=259200&X-Amz-Credential=AKIA3FLDXG3Y26KRHM4L%2F20260514%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Signature=c3f8e87f61b24e6a46062cefea907a4bb9e995527097d6137acb0f3d972bf2e4"
            },
            "expiredTime": "600",
            "payType": "BARCODE",
            "paymentRedirectUrl": "https://oslpay-test1-web-ramp-qa.glb.osl-nucleus.io/startPay?sessionId=$5$pluto$b4q09iNxgqzSt/bbG/T1YpioRWnif.WHdlQVo8jhsP.",
            "url": null,
            "va": null
        }
    },
    "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"
}