1. Webhooks & Events
BPN OpenAPI
  • Getting Started
    • Introduction to BPN
    • Architecture Overview
  • Onboarding & Prerequisites
    • Product Demo
    • Sandbox & Test Environment
    • Signature Authentication Mechanism
  • Use Case
    • Fiat Collection via Virtual Accounts & Local Collection Rails
    • Convert Stablecoins
    • Transfer
    • Wallet
    • Compliance (Travel Rule Information Submission)
    • RFI process
  • API Reference
    • KYB
      • Share KYB/KYC Info For Sub User
      • Get KYB Sub User Status
    • Account Management
      • Create Virtaul Account
      • List Primary Virtual Accounts
      • Inquiry Master VA Balance
      • Add Bank Account
      • List Bank Account
      • List Banks
      • Submit Invoice
      • Send Email Verify Code
      • List Virtual Account
      • List RFI Sub Virtual Account
      • Sub Virtual Account Submit RFI
      • Get Sub Virtual Account RFI Template Info
      • Get Virtual Account Detail
      • List Virtual Account Orders
      • Get Virtual Account Order Detail
      • Refund Virtual Account Order
      • Payout via Virtual Account
      • Query bank info by account number
      • Get Invoice list Status
    • Wallet
      • List Wallets
      • List Sub Account
      • Create Sub Account
      • Wallet Internal Transfer
      • Query Wallet Internal Transfer Detail
      • List Wallet Internal Transfer order
      • Get Asset Balance
      • Get Deposit Address
      • Add Deposit Sender
      • Add Withdraw Whitelist
      • Delete Withdraw Whiltelist
      • Request Withdraw
      • Query Transaction History
    • FX
      • Inquery FX Rate
    • BPN Transactions
      • Get Stablecoin Quote
      • Create Stable Order
      • Get Order (Single) Detail
      • List Orders(Batch)
    • Reconciliation
      • Get Reconciliation Order List By Page
    • Crypto Payment Link
      • Abnormal fund
        • Abnormal funds
        • Matchable Payment links
        • Link Order(Abnormal fund)
        • Submit to platform
      • Create Payment Link
      • List Payment links
      • Get Curreny Network Config
    • External Provider
      • FE: Create stablecoin collection sub-account link
      • Create stablecoin collection sub-account link
      • Get sub wallet account
      • List sub wallet accounts
      • Change sub wallet status
      • Query stablecoin collection order list
      • Query stablecoin collection order status
      • Get quote
      • Accept stablecoin collection order
      • Reject stablecoin collection order
      • Query USD balance
      • USD Payout (withdrawal)
      • Query USD Payout order status
      • Query USD Payout order list
    • Mock
      • Mock Virtual account Payin Order
      • Mock wallet deposit transaction
      • Mock Payin Order Refund
      • Mock Payout Order Update Status
    • Pay Session
      • Request Pay Session
  • Webhooks & Events
    • Resend Fail Webhook
      POST
    • Open Virtual Account Status
    • Virtual Account Payment Status
    • Virtual Account Invoice update
    • Transaction Status Notification
    • KYB Status Notification
    • Deposit Sender Detail Required
    • Abnormal Fund Notification
    • WALLET_TRANSFER_ORDER_UPDATED
    • Sub Virtual Account RFI Required
    • Sub Wallet Update
    • wallet address whitelist Copy
    • Crypto Deposit Order Notification
    • Collect Order Updated
    • Usd Payout Order Updated
    • onboard result
    • partner order status
    • PaymentLink Notification
  • Partner Flow
    • inquiry supported currency
    • KYC/KYB Sharing
    • inquiry onboard status
    • get price
    • Create order
    • order list
    • inquiry single order
    • get user deposit address
    • get payment instruction
    • sync fund notification
    • daily settlement records
  • Appendix
    • Enum
    • Virtual Account(VA) Support List & KYB requirement
  • Release Notes & Changelog
  1. Webhooks & Events

Virtual Account Payment Status

Webhook
POST
stablecoinPaymentResult
Latest Update Date: 23 July, 2026
The webhook notifies merchants when a Virtual Account PayIn or Payout order status changes.
Use data.orderId as the unique identifier for tracking the same order throughout its lifecycle.
Use data.orderStatus as the current business status.
Process notifications idempotently.
PROCESSING is an intermediate status used only for Payout.
REFUNDING is an intermediate status used only after a PayIn refund is initiated.

Status Definitions#

StatusApplicable Transaction TypeDescription
INITPayoutThe Payout is initialized.
PROCESSINGPayoutThe Payout is being processed.
SUCCESSPayIn, PayoutThe PayIn or Payout has completed successfully at the current stage.
FAILPayoutThe Payout failed before reaching SUCCESS. This status currently applies only to Payout.
REFUNDINGPayIn refundA PayIn refund has been initiated and is being processed asynchronously. This status applies only to PayIn refunds.
REFUNDEDPayIn refund, Payout returnPayIn: The refund completed successfully. Payout: The initially successful Payout was later returned.
REFUND_FAILPayIn refundThe PayIn refund could not be initiated or completed.
RETURNEDSystem-returned PayInThe PayIn was returned by the system before the funds were credited to the balance, for example because RFI or limit requirements were not satisfied. The funds are returned to the payer through the original channel, the balance remains unchanged, and only one order is notified with RETURNED; no new order is created.
RETURNED is reserved and will not be emitted
Effective immediately, RETURNED will no longer appear in webhook notifications. The status remains reserved in the system design but must not be expected or used in the current merchant integration flow.
SUCCESS may be followed by a refund-related status.

PayIn and PayIn Refund#

1. Normal PayIn Flow#

A PayIn is notified directly as SUCCESS and does not enter PROCESSING.
SUCCESS

2. Merchant-Initiated Refund#

A merchant may request a refund after the PayIn has reached SUCCESS.
When the refund debits an already-credited balance, the system creates a separate refund order with businessType = PAYIN_REFUND and links it to the original PayIn through data.originalOrderId.

Refund Status Flow#

Original PayIn SUCCESS → PAYIN_REFUND REFUNDING → REFUNDED
Original PayIn SUCCESS → PAYIN_REFUND REFUNDING → REFUND_FAIL
Original PayIn SUCCESS → PAYIN_REFUND REFUND_FAIL
The direct REFUND_FAIL outcome may occur when the refund fails before entering asynchronous processing.

Refund Order Identifiers#

For a PAYIN_REFUND webhook notification:
data.orderId: The new PAYIN_REFUND order ID.
data.originalOrderId: The original PayIn order ID.
data.orderStatus: The current refund status.

Manual Handling After REFUND_FAIL#

REFUND_FAIL means that the refund attempt failed and requires manual intervention. It does not invalidate the original successful PayIn.
The manual review may result in either:
1.
Continue the refund: The refund is retried or otherwise completed successfully, and the refund status progresses to REFUNDED.
REFUND_FAIL → Manual intervention → REFUNDED
2.
Keep the PayIn successful: The parties agree not to continue the refund. No refund is completed, and the original PayIn remains SUCCESS.
REFUND_FAIL → Manual decision: no refund → Original PayIn remains SUCCESS
[!IMPORTANT]
In the second outcome, the PayIn does not transition from REFUND_FAIL back to SUCCESS. The original PayIn remains successful, while the failed refund attempt is closed without completing a refund.

3. System-Initiated Limit Return#

The system retains a return mechanism for a PayIn that exceeds an account or transaction limit.
Under the current system design, this scenario is not expected to occur. If the reserved mechanism is triggered:
The flow uses only one transaction record, and no new order is created.
If the return succeeds, the status is RETURNED.
If the return fails, the status is RETURNED_FAIL and manual intervention is required.
During normal return processing, no SUCCESS notification is sent.

Manual Handling After RETURNED_FAIL#

The manual review may result in either:
1.
Continue the return: The return is retried or otherwise completed successfully, and the status progresses to RETURNED.
RETURNED_FAIL → Manual intervention → RETURNED
2.
Accept the PayIn as successful: The parties agree not to continue the return, and the status progresses to SUCCESS.
RETURNED_FAIL → Manual decision: accept PayIn → SUCCESS
When the final status is RETURNED, the funds are returned before being credited to the balance and the balance remains unchanged.

Payout and Payout Refund#

1. Normal Payout Flow#

PROCESSING → SUCCESS
PROCESSING → FAIL
PROCESSING: The Payout is being processed.
SUCCESS: The Payout was initially completed successfully.
FAIL: The Payout failed before reaching SUCCESS.

2. Bank Return After Success#

A receiving bank, downstream bank, or payment institution may reverse or return a Payout after it has reached SUCCESS.
When the return is confirmed:
1.
The returned amount is credited back to the merchant's account balance.
2.
The original Payout order is updated from SUCCESS to REFUNDED.
3.
A separate PAYOUT_REFUND order is created with status SUCCESS.
This process therefore uses two linked orders:
1.
The original Payout order.
2.
The newly created PAYOUT_REFUND order.

3. Order Status and Webhook Flow#

Original Payout Order#

PROCESSING → SUCCESS → REFUNDED
The transition from SUCCESS to REFUNDED is an internal update. No webhook notification is sent for this status change.

Payout Refund Order#

SUCCESS
The new PAYOUT_REFUND order is created with status SUCCESS, and a webhook notification is sent for this refund order.
[!IMPORTANT]
Merchants receive only the webhook for the new PAYOUT_REFUND order. The original Payout order's transition to REFUNDED does not send a webhook.

4. Linked Order Identifiers#

The originalOrderId field links the original Payout order and the generated Payout refund order.

PAYOUT_REFUND Webhook#

data.orderId: The new PAYOUT_REFUND order ID.
orderStatus: SUCCESS.
data.originalOrderId: The original Payout order ID.

Original Payout Order After Refund#

orderId: The original Payout order ID.
orderStatus: REFUNDED.
originalOrderId: The newly created PAYOUT_REFUND order ID.
No webhook notification is sent for this original Payout order status update.

5. Merchant Handling#

For each webhook notification:
1.
Locate the corresponding order by data.orderId.
2.
Apply data.orderStatus to that order.
3.
Ensure repeated notifications do not cause duplicate status or balance updates.

Request

Header Params

Body Params application/json

Examples
{
    "eventId": "b271f38a-c45f-4348-ab04-0807be30c9d7",
    "eventType": "VIRTUAL_ACCOUNT_ORDER_UPDATED",
    "referenceId": "VA20251123093831400002",
    "status": "SUCCESS",
    "timestamp": 1763890710735,
    "data": {
        "requestId": "PAYOUT20250828150000812",
        "taskId": "",
        "orderId": "VA20251123093831400002",
        "businessType": "CASH_OUT",
        "currency": "BRL",
        "amount": 360.0,
        "orderStatus": "SUCCESS",
        "failCode": "",
        "failReason": "",
        "fromAccountId": "4",
        "fromAccountName": "3322",
        "fromAccountNumber": "",
        "fromAccountCnpjOrCpf": "03027123383271",
        "fromAccountBankCode": "",
        "toAccountId": "",
        "toAccountName": "Teste da Silva LTDA",
        "toAccountNumber": "1234567890",
        "toAccountCnpjOrCpf": "12345678912",
        "toAccountBankCode": "13140088",
        "transactionId": "EECU20251123093831400002",
        "createTime": 1763890682000,
        "completeTime": 1763890710679,
        "invoiceSubmitStatus": "",
        "invoiceId": ""
    }
}

Request Code Samples

Shell
JavaScript
Java
Swift
Go
PHP
Python
HTTP
C
C#
Objective-C
Ruby
OCaml
Dart
R
Request Request Example
Shell
JavaScript
Java
Swift
cURL
curl --location 'https://your-api-server.com' \
--header 'API-KEY: apikey ' \
--header 'API-TIMESTAMP: 1765454475759' \
--header 'API-SIGNATURE: d110004016a3cf2bfa06d57eaf850618c143f8aeb6e528882bd3811b2b988160' \
--header 'X-BPN-API-KEY: <api-key>' \
--header 'X-BPN-SIGNATURE: <api-key>' \
--header 'X-BPN-TIMESTAMP: <api-key>' \
--header 'Content-Type: application/json' \
--data '{
    "eventId": "b271f38a-c45f-4348-ab04-0807be30c9d7",
    "eventType": "VIRTUAL_ACCOUNT_ORDER_UPDATED",
    "referenceId": "VA20251123093831400002",
    "status": "SUCCESS",
    "timestamp": 1763890710735,
    "data": {
        "requestId": "PAYOUT20250828150000812",
        "taskId": "",
        "orderId": "VA20251123093831400002",
        "businessType": "CASH_OUT",
        "currency": "BRL",
        "amount": 360.0,
        "orderStatus": "SUCCESS",
        "failCode": "",
        "failReason": "",
        "fromAccountId": "4",
        "fromAccountName": "3322",
        "fromAccountNumber": "",
        "fromAccountCnpjOrCpf": "03027123383271",
        "fromAccountBankCode": "",
        "toAccountId": "",
        "toAccountName": "Teste da Silva LTDA",
        "toAccountNumber": "1234567890",
        "toAccountCnpjOrCpf": "12345678912",
        "toAccountBankCode": "13140088",
        "transactionId": "EECU20251123093831400002",
        "createTime": 1763890682000,
        "completeTime": 1763890710679,
        "invoiceSubmitStatus": "",
        "invoiceId": ""
    }
}'

Responses

🟢200OK
application/json
Bodyapplication/json

Example
"string"
Previous
Open Virtual Account Status
Next
Virtual Account Invoice update
Built with