POST /v1/stable/order/submit-order — create a stablecoin → fiat order from an unexpired quote. Fiat is paid out to targetAccountId (bound via add-account), taken from this request or, if omitted, from the quote.| paymentMethod | KES | UGX | TZS |
|---|---|---|---|
MOBILE_MONEY | ✅ | ✅ | ✅ |
BANK_TRANSFER | ✅ | ❌ | ❌ |
| field | required | value |
|---|---|---|
businessType | yes | STABLE_SELL |
paymentMethod | yes | BANK_TRANSFER / MOBILE_MONEY |
sourceAccountId | no | null (or omit) for STABLE_SELL |
sourceCurrency | yes | USDC / USDT |
targetCurrency | yes | KES / UGX / TZS |
targetAccountId | no | accountId returned by POST /v1/account/add-account; omit to use the targetAccountId sent in get-quote |
quoteId | yes | quoteId returned by get-quote; must not be expired |
merchantOrderId | yes | your order number; unique per order, used for idempotency and reconciliation |
transactionId | yes | id of the transfer that moved the end user's funds to the BPN account: the on-chain transaction hash, or your internal transfer id. Each transactionId can be used for one order only |
userRefundCryptoAddress | yes | where stablecoin is refunded if the order cannot be completed |
userRefundCryptoAddress.address | yes | on-chain address |
userRefundCryptoAddress.network | yes | chain network, e.g. TRON, ETH |
userRefundCryptoAddress.currency | no | USDC / USDT; defaults to sourceCurrency |
userRefundCryptoAddress.memo | no | tag / memo for chains that require it |
remarks | no | free text |
businessType, paymentMethod, sourceCurrency and targetCurrency must match the values used in get-quote for quoteId.targetAccountId in get-quote or in submit-order (at least one of them). If submit-order omits it, the targetAccountId of the quote is used; if both are sent, the submit-order value is used. The account must have the same paymentMethod + targetCurrency as the quote.transactionId is required for KES / UGX / TZS. Send either the on-chain transaction hash or your internal transfer id of the transfer that moved the end user's funds to the BPN account.transactionId can be used for one order only. Submitting a new merchantOrderId with a transactionId that is already linked to another order is rejected (status != 200).beneficiaryBank for KES / UGX / TZS: the payout account is targetAccountId.merchantOrderId is the idempotency key. Retrying with the same merchantOrderId (e.g. after a timeout) does not create a second order; query the order with GET /v1/stable/order/query-detail?merchantOrderId=... instead of submitting a new one. Do not reuse a merchantOrderId for a different order. transactionId is not an idempotency key: a retry must send the same merchantOrderId and the same transactionId.datamerchantOrderId, orderId, orderStatus (see query-detail for all statuses).GET /v1/stable/order/query-detail (and the order status webhook).targetAccountId is validated before the order is created:ODR00001047 — bank code is not in the quote channel's currency catalog.ODR00001048 — bank catalog is unavailable; the order is not created.ODR00001049 — beneficiary account format is invalid.quoteId, merchantOrderId, userRefundCryptoAddress, optional beneficiaryBank (HK clearing code in bankId; omit to use the KYB default) and extInfo.travelRule. redirectUrl / nextAction / externalOrderId in the response are HKD only.