Skip to main content

Cashout

Cashout pays fiat directly to a recipient's bank account, debiting your crypto fund. There is no QR code: you name the recipient yourself.

  1. Quote it — review-cashout.
  2. Create it — create-cashout.
  3. Track it — orders/details, plus a webhook.

The user must be KYC-verified first. See KYC. Cashout and scan-to-pay share the same order lookup, the same order object, and the same webhook.

The published signature string for create-cashout was wrong

Older documentation gives the signed string for create-cashout as partnerCode|externalOrderId|currency|fiatAmount|bankCode|bankAccountNumber|content|userEmail|secretKey.

That is scan-to-pay's string, pasted into the cashout page. It has never been what the server checks. The real one is below: different fields, different order, and the bank details are not signed at all.

If your cashout integration fails with INVALID_SIGNATURE and you cannot see why, this is why.

Quote a cashout

POST /api/v2/orders/review-cashout

Signed string

partnerCode|userEmail|fiatCurrency|fiatAmount|cryptoCurrency|<secretKey>

Request — every field is required.

FieldTypeNotes
partnerCodestring
userEmailstringThe end user.
fiatCurrencystringVND, PHP, IDR
fiatAmountnumberWhat the recipient receives.
cryptoCurrencystringThe crypto to debit — USDT.
signaturestring

Response data

FieldTypeNotes
fiatAmountnumberEchoed from your request.
currencystringThe crypto code. Some schemas type this as a number; it is a string.
pricenumber
tokenAmountnumberThe crypto that will be debited.
totalPaymentnumber
processingFee, systemFeenumber
signaturestringOver fiatAmount|currency|price|tokenAmount|totalPayment|secretKey.

The quote does not return the minimum and maximum amounts, though v3's does. Handle 24 and 25 instead, whose messages carry the live limit.

Errors46 SERVICE_NOT_AVAILABLE, 24 / 25 (below min / above max), 16 AMOUNT_OUT_OF_RANGE (below the payment provider's own floor, 10,000 VND for Vietnam), 7 PARTNER_INSUFFICIENT_BALANCE, 9 SERVICE_UNDER_MAINTENANCE, plus the KYC codes (34, 59, 60, 61, 62, 64) and the shared ones.

Create a cashout

POST /api/v2/orders/create-cashout

Signed string

partnerCode|externalOrderId|userEmail|cryptoCurrency|fiatCurrency|fiatAmount|<secretKey>

It signs cryptoCurrency (not currency) and fiatCurrency, and it does not sign the recipient's bank details: those live in extendInfo, outside the signature.

Every segment is always interpolated, so omitting fiatCurrency or fiatAmount — neither of which is rejected as missing — puts the literal text undefined into the string. The signature still verifies, because you and the server build it the same way, and the order then fails with 46. Send both.

Request

FieldTypeRequiredNotes
partnerCodestringYes
externalOrderIdstringYesYour unique id, and the idempotency key.
userEmailstringYesMust be KYC-verified.
cryptoCurrencystringYesThe crypto to debit — USDT.
fiatCurrencystringYesOmitting it is not rejected as a missing field, and no default is applied. See above.
fiatAmountnumberYesSame.
extendInfoobjectYesThe recipient.
webhookSecretKeystringNoOmit it and no callback is ever sent.
signaturestringYes

extendInfo — the recipient

FieldTypeRequiredNotes
recipientNamestringYes
recipientNumberstringYesThe bank account number.
recipientBankCodestringYesFrom the bank list.
transferContentstringNoThe transfer remark. Unaccented letters only, where the provider checks it.
recipientTypestringNoIf you send it, it must be a recognised value — otherwise 5.
JSON
{
"partnerCode": "ACME",
"externalOrderId": "acme-co-1042",
"userEmail": "[email protected]",
"cryptoCurrency": "USDT",
"fiatCurrency": "VND",
"fiatAmount": 5000000,
"webhookSecretKey": "81663eae...",
"extendInfo": {
"recipientName": "NGUYEN VAN A",
"recipientNumber": "0123456789",
"recipientBankCode": "VCB",
"transferContent": "ALIX CASHOUT 1042"
},
"signature": "I7XDfTngWIraq...=="
}

Response datathe order object, with type: "CASHOUT".

For a cashout, tokenTransfer.address, tokenTransfer.network and bankTransfer.qrUrl are null — there is no on-chain leg to the recipient and no QR to show.

Errors — everything review-cashout can return, plus:

11 DUPLICATE_TRANSACTION_IDexternalOrderId is already used.
13 WEBHOOK_SECRET_NOT_FOUNDwebhookSecretKey matches no registered endpoint. Checked before anything else, so the order is not created.
18 INVALID_BANK_ACCOUNTextendInfo is missing, or the account could not be verified.
56 BANK_NOT_SUPPORTEDrecipientBankCode is not in the provider's list.
17 INVALID_TRANSFER_CONTENTtransferContent has accents or special characters.
27 USER_EMAIL_INVALIDuserEmail is malformed.
5 INVALID_REQUEST_DATArecipientType is not a recognised value.
12 TRANSACTION_CREATE_FAILEDThe payout failed after the order was created. You are already refunded, and data holds the order.