Payout V2 Integration Guide

When a BaaS merchant needs to initiate cross-border or local payouts from a fiat balance (va_group) or a crypto wallet balance (cw), the payout order can be created through /open-api/v2/payout/create. This endpoint supports channel pre-check, channel-specific party fields, source-of-funds validation for individual accounts, and multiple payout product channels.

📘

Scope

This guide covers the OpenAPI V2 Create Payout endpoint. Debit balances are limited to va_group (fiat) and cw (crypto wallet); ca is not supported. Payee, balance, payout detail, and file upload capabilities remain under the /open-api/v1/* paths.


Prerequisites 📋

ItemDescription
API base URL<BASE_URL> for sandbox and production is provided by the account manager
appid / secretUsed to obtain an access token and verify webhook signatures
dgp-entity-idEntity account UUID. Pass it in the header when required by the endpoint
Webhook URLA publicly accessible HTTPS URL for payout status notifications
IP whitelistIf IP whitelisting is enabled, the caller's outbound IP must be whitelisted
KYC statusThe corresponding entity must complete KYC and have a normal account status
Product activationPayout APIs are available only to merchants with BaaS API activated

Recommended Integration Flow ⚙️

  1. Obtain an access token.
  2. Query the supported payee options.
  3. Create a payee and save the payeeId.
  4. Upload payout materials or source-of-funds proof and save the fileId.
  5. Query available payout balances and save the balanceId.
  6. Call POST /open-api/v2/payout/create.
  7. If PARTY_FIELDS_REQUIRED is returned, retry with the same callId and the collected partyFields.
  8. If PAYOUT_CREATED is returned, save the payoutTrxId and idNo.
  9. Track the final status through the payout detail endpoint or webhooks.

Shared V1 Endpoints (Pre-Payout Preparation) 🔍

Endpointdgp-entity-id
GET /open-api/v1/payout/payee-optionsNot required
POST /open-api/v1/payout/payeeOptional
PUT /open-api/v1/payout/payeeOptional
POST /open-api/v1/file/uploadOptional
GET /open-api/v1/payout/accountsRequired
POST /open-api/v2/payout/createRequired
GET /open-api/v1/payout/detailRequired

Query Payee Options

GET /open-api/v1/payout/payee-options

Call the Get payee options endpoint to retrieve the supported bank countries and currencies.


Create Payee

POST /open-api/v1/payout/payee

Call the Create payee endpoint to register the target account. After a successful creation, save the returned payeeId.

The dgp-entity-id header is optional for this endpoint. Pass it when the payee should be attached to a specific entity.

FieldRequiredDescription
currencyYesPayee currency
countryYesBank country, ISO 3166-1 alpha-2
firstNameConditionalFirst name for an individual payee
lastNameYesLast name or company name
accountNumberYesBank account number
bankNameYesBank name
bankCodeConditionalLocal clearing code, used for ACH / WIRE
bicSwiftConditionalSWIFT / BIC, required for SWIFT payouts
payeeAddressYesPayee address
bankAddressNoBank address
labelNoLabel or note
sortCodeConditional: required for UKUK Sort Code, required for UK payments only

Routing parameter rules: ACH / WIRE require bankCode and accountNumber; SWIFT requires bicSwift, and providing both bankAddress and payeeAddress is recommended.

{
  "currency": "USD",
  "country": "US",
  "firstName": "John",
  "lastName": "Doe",
  "accountNumber": "31139750168449",
  "bankName": "JP Morgan Chase NA",
  "bankCode": "028000024",
  "bicSwift": "CHASUS33",
  "payeeAddress": {
    "line1": "123 Business Rd",
    "postCode": "10001",
    "city": "New York",
    "state": "NY",
    "country": "US"
  },
  "bankAddress": {
    "line1": "270 Park Avenue",
    "postCode": "10017",
    "city": "New York",
    "state": "NY",
    "country": "US"
  },
  "label": "Supplier Q3"
}

Upload Payout Materials and Source-of-Funds Proof

POST /open-api/v1/file/upload (multipart/form-data)

Call the Upload File endpoint to upload the materials. After a successful upload, save the returned fileId.

The dgp-entity-id header is optional for this endpoint.

Form FieldRequiredDescription
fileYesThe file itself
fileUseTypeYespayout or source_of_funds, among others
fileNameYesFile name

Response example:

{
  "data": {
    "fileId": "example_file_uuid"
  }
}

Payout proof file IDs are passed in paymentMaterial; for individual accounts, source-of-funds proof file IDs are passed in sourceOfFundsMaterial and uploaded with fileUseType=source_of_funds.


Query Available Payout Balances

GET /open-api/v1/payout/accounts

Call the Query payout accounts endpoint. The dgp-entity-id header is required.

Response FieldDescription
idBalance ID, used as balanceId when creating a payout
availableBalanceAvailable balance
currencyCurrency
typeva_group (fiat) or cw (crypto wallet)
⚠️

paymentCurrency Must Match balanceId

paymentCurrency must match the currency of the selected balanceId; otherwise, the payout cannot be debited.


Create Payout Endpoint 🔗

POST /open-api/v2/payout/create
ParameterLocationRequiredDescription
AuthorizationheaderYesBearer <access_token>
dgp-entity-idheaderYesEntity account UUID
callIdbodyYesIdempotency ID, 6~50 characters, consumed only when the payout is created
payeeIdbodyYesPayee ID
receiveTypebodyYesSWIFT / ACH / WIRE
channelCodebodyYesPayout product code; only _01 codes are accepted
balanceIdbodyYesDebit balance ID; only va_group or cw is supported
paymentCurrencybodyYesDebit currency; must match the balanceId currency
payeeCurrencybodyYesPayee currency
paymentAmountbodyOne of twoFixed payment amount
payeeAmountbodyOne of twoFixed payee amount
purposebodyYesPayment purpose code
paymentMaterialbodyYesArray of fileId values for payout proof, 1~10 files
partyFieldsbodyNoChannel-specific fields; keys must match fields[].key
reasonbodyNoNote, up to 140 characters
costBornebodyNoSHA / OUR, applied according to channel rules
sourceOfFundsbodyConditionalRequired for individual accounts
sourceOfFundsMaterialbodyConditionalRequired for individual accounts; uploaded with fileUseType=source_of_funds

Only one of paymentAmount and payeeAmount can be provided. The payment currency and payee currency may differ; the system automatically obtains a quote for cross-currency payouts.


Payout Product Codes channelCode 📦

channelCodeInternal ChannelChannel NameExecution ModeNotes
va_005_01va_005ZenusDepends on route config, mostly automaticExisting, unchanged
va_007_01va_007EpayDepends on route configExisting, unchanged
va_008_01va_008TazapayDepends on route configExisting, unchanged
va_012_01va_012PaveUsually offline customer operationsNew, corridor aligns with Web
va_015_01va_015QPayUsually offline customer operationsNew, corridor aligns with Web; full amount semantics
❗

Only Pass _01 Product Codes

Requests only accept va_005_01, va_007_01, va_008_01, va_012_01, and va_015_01. Passing internal codes such as va_012 will be rejected by parameter validation.

⚠️

Channel Availability Follows Route Config

Available currencies, countries, and receive types align with the platform's Web payout routes. There is currently no endpoint to query available channelCode values.

⚠️

va_012 and va_015 Are Offline Channels

After creation, va_012_01 (Pave) and va_015_01 (QPay) are processed through offline customer operations. Settlement timing depends on the offline processing result and is not real-time channel payout. This document does not promise a fixed timeline.


Cost Borne 🧾

channelCodereceiveTypecostBorne Behavior
va_005_01 / va_007_01 / va_015_01AnyNo field semantics; the value is stored as null even if provided
va_008_01 / va_012_01SWIFTSHA or OUR is allowed; omit to default to SHA; OUR charges the full-amount surcharge
va_008_01 / va_012_01Non-SWIFTForced to SHA; OUR is not allowed

Source of Funds for Individual Accounts 💰

sourceOfFundsMeaning
1Salary
2Investment income
3Property transfer
4Self-employment income
5Other

Individual accounts must provide both sourceOfFunds and sourceOfFundsMaterial; business accounts may provide them optionally.


Response Handling 🚀

Scenario A: Party Fields Required

When data.status = PARTY_FIELDS_REQUIRED, the payout order has not been created, no funds have been debited, and the callId has not been consumed. Render the form based on the returned fields[], collect the missing values, and retry with the same callId.

{
  "code": 0,
  "data": {
    "status": "PARTY_FIELDS_REQUIRED",
    "fields": [
      {
        "key": "receiver_mobile",
        "label": "Payee mobile",
        "type": "text",
        "required": true,
        "order": 1
      },
      {
        "key": "sender_beneficiary_relationship",
        "label": "Relationship to payee",
        "type": "select",
        "required": true,
        "order": 2,
        "options": [
          { "label": "Family", "value": "01" },
          { "label": "Friend", "value": "02" }
        ]
      }
    ]
  }
}
fields[].typeValue to Submit in partyFields
textString
selectSelected option's value, not label
dateDate string according to dateConfig.valueFormat
fileCall /open-api/v1/file/upload first, then submit the returned fileId
💡

Common QPay Party Field

va_015_01 pre-check may require receiver_bank_address, which is used to populate the receiving bank address. If this field is missing, the endpoint returns PARTY_FIELDS_REQUIRED; no funds are debited and the callId is not consumed. Retry with the same callId after providing the field.


Scenario B: Payout Created

{
  "code": 0,
  "data": {
    "status": "PAYOUT_CREATED",
    "payoutTrxId": "example_payout_trx_id",
    "idNo": "PO202610130001",
    "quoteId": "example_fx_quote_id",
    "fee": 15.00,
    "feeCurrency": "USD"
  }
}
⚠️

Check That status Is PAYOUT_CREATED

The response must be validated by checking data.status === "PAYOUT_CREATED". Do not judge success solely by whether payoutTrxId is present. Once created, funds are locked or debited; the final result is determined by the detail endpoint or webhook.


Query and Async Notifications 🔔

Debit SourceQuery MethodWebhook Events
Fiat va_groupGET /open-api/v1/payout/detail?payoutTrxId=...va.transaction.create, va.transaction.update
Crypto cwGET /open-api/v1/payout/detail?payoutTrxId=...crypto.wallet.transaction, crypto.wallet.transaction.update

The dgp-entity-id header is required when querying payout details. For fiat VA payout events, refer to VA Transaction Events. For crypto wallet payout events, refer to Crypto Wallet Transaction Events.

Webhooks use HMAC signature verification. Delivery retry intervals are 0s, 10s, 30s, 60s, 2min, 5min, and 10min, up to 7 attempts. The receiver must return 2xx within 30 seconds, and processing must be idempotent.

For crypto wallet payouts, the webhook data.id is the crypto transaction ID, which differs from payoutTrxId. Use callId or payout/detail for reconciliation.


Idempotency and Concurrency 🛡️

MechanismDescription
callIdUnique within the same entity; repeated submission after a successful creation is rejected
Party Fields retryThe callId is not consumed during the party-fields stage; retry until PAYOUT_CREATED
ConcurrencyConcurrent submissions with the same callId allow only one order to take effect

Integration Examples 🧪

Create Payout (Pave / offline)

{
  "callId": "merchant-payout-demo-001",
  "payeeId": "<payee-uuid>",
  "receiveType": "SWIFT",
  "channelCode": "va_012_01",
  "balanceId": "<va_group-or-cw-balance-uuid>",
  "paymentAmount": 100,
  "paymentCurrency": "USD",
  "payeeCurrency": "USD",
  "purpose": "13",
  "paymentMaterial": ["<attachment-uuid>"],
  "costBorne": "OUR",
  "reason": "invoice 12345"
}

Create Payout (QPay; party fields may be provided after the first response)

{
  "callId": "merchant-payout-demo-002",
  "payeeId": "<payee-uuid>",
  "receiveType": "SWIFT",
  "channelCode": "va_015_01",
  "balanceId": "<balance-uuid>",
  "payeeAmount": 100,
  "paymentCurrency": "USD",
  "payeeCurrency": "USD",
  "purpose": "13",
  "paymentMaterial": ["<attachment-uuid>"],
  "partyFields": {
    "receiver_bank_address": "1 Example Street, City"
  }
}

receiveType 📱

ValueDescription
SWIFTInternational SWIFT
ACHUS ACH
WIREUS Wire

Integration Notes 🧭

  • Complete end-to-end integration testing in the sandbox environment first.
  • Save payoutTrxId; CW orders cannot use vaTrxId to query details.
  • Use both webhooks and payout/detail as a double check for the final status.
  • Pass amounts with the correct decimal precision for the currency.
  • Upload materials first to obtain fileId, then pass them to the create payout endpoint.
  • Mask and redact secrets, tokens, and account information in logs.
  • Follow the per-endpoint dgp-entity-id requirements: it is required for Create Payout, Query Accounts, and Query Payout Detail; optional for Create/Update Payee and File Upload.
  • If a channel combination or product code cannot be determined, compare it with the Web payout route or contact the account manager.

Core Constraints and Rules 🛡️

❗

Internal Channel Codes Are Prohibited

Internal channel codes such as va_012 and va_015 cannot be used in requests. Only _01 product codes are accepted.

⚠️

paymentCurrency Must Match balanceId

paymentCurrency must match the currency of the selected balanceId; otherwise, the payout cannot be debited.

⚠️

ca Is Not Supported

balanceId only supports va_group and cw; ca is not supported.

🚧

Do Not Expect Real-Time Channel Payout

va_012_01 and va_015_01 are processed through offline customer operations after order creation. Final settlement timing depends on the offline processing result.


Did this page help you?