crypto_to_fiat order for each payout. VelaFi converts the crypto at the quoted rate and sends the fiat through the local rail (for example SPEI, Pix, CVU, Bre-B) or through Circle Payment Network (CPN) for USD and EUR.
For the full field reference, see the Off-Ramp guide and Create an Off-Ramp Order.
How it works
Before you start
- API access. An API key, your server IPs on the whitelist, and a business token for the
X-BH-TOKENheader. See Authentication. - A verified merchant with an activated account for the payout currency and channel. The beneficiary must use a payment method (
paymentId) of that account. See Activate a Merchant Account. ORDER_WEBHOOKregistered. Your endpoint must return HTTP2xxwith the body{"callbackStatus":"SUCCESS"}, or the delivery counts as failed. See Webhooks and Delivery and Retries.
Build the flow
1
Fund the merchant's crypto wallet
Get the merchant’s deposit address for the token and network you will send:Send the crypto to
address (with addressExt if needAddressTag is true), at least minQuantity. See Get Crypto Deposit Address for the supported token and network pairs.There is no webhook for incoming crypto deposits. Confirm the credit with Get Crypto Deposit History (status 2 is successful) and check the spendable amount in free from Get Crypto Balance.2
Register the beneficiary
Get the field template for the payout method with Store
GET /v2/payments/templates?paymentId={paymentId}, then submit the filled-in fields with POST /v2/payments. Use the same merchantId as your orders.Response
data.id. It is the userPaymentId for your orders. Only a beneficiary in status 1 (valid) can receive payouts. 2 means verification is still in progress. 3 means it failed: read failReason, and if the account already exists, use sameAccountNumberId instead of creating a new record. Register a beneficiary once and reuse it. See Manage Beneficiaries.3
Optional: lock the rate with a quote
Call Get a Crypto/Fiat Quote with A
from set to the crypto, to set to the fiat and createQuoteId=true. The price is fiat per 1 unit of crypto.quoteId is valid for 10 seconds in production (300 seconds in the sandbox), is single-use, and is bound to the country, crypto and fiat it was issued for. Without a quoteId, the order is priced when it is created.4
Create the payout order
Call How the order is funded. The crypto comes from the merchant’s wallet balance for that
POST /v2/order/crypto_to_fiat with the beneficiary’s userPaymentId. Use the country and fiat of the beneficiary. Send fiatAmount to fix what the beneficiary receives, or cryptoAmount to fix what you spend. If you send both, fiatAmount wins. Always send a unique clientId.Response
crypto. VelaFi debits it when it creates the order, and the order moves to 40 (or 10, 11 or 31 first when review or documents are required). If the balance is too low, the request is rejected with an insufficient-balance error and no order is created.Post-funded variant. If VelaFi has enabled post-funded off-ramp for your account (it can differ by fiat currency), an underfunded order is accepted and waits in 32 (Awaiting crypto deposit). Order detail then returns orderExpireTimestamp, the deadline in Unix seconds. Deposit enough crypto to the wallet before the deadline. VelaFi checks the balance every minute, applies it to waiting orders oldest first, and moves a covered order to 40. If the deadline passes first, the order is cancelled (70). See Funding the order.If the request times out, look the order up by clientId before you retry. See Idempotency and retries.5
Track the payout to completion
Wait for
ORDER_WEBHOOK events (orderType is SELL), or poll GET /v2/order/detail?orderType=crypto_to_fiat&clientId={clientId}.traceNumber holds the central bank reference number for the payout when the rail returns one. Share it with the beneficiary if they ask for proof of payment.USD and EUR payouts through Circle Payment Network
CPN carries USD and EUR payouts. It is payout only: you cannot collect funds or createfiat_to_crypto orders on CPN.
- Activate a CPN account for the merchant and currency with
trenchset toCircle Payment Network. See Activate a Merchant Account for a Business. - Register the beneficiary on a CPN payment method, for example
120Wire (CPN) or127FEDWIRE (CPN) for USD and124SEPA (CPN) for EUR. CPN templates ask for the beneficiary’s address, auseCaseand areasonForPaymentcode. See the CPN templates in Get Payment Templates. - Create the
crypto_to_fiatorder withfiatset toUSDorEURand the CPN beneficiary’suserPaymentId, exactly as above.
11). When it does, upload the requested documents with Upload Documents. The order then moves to 12 and waits for a decision.
Webhooks to handle
ORDER_WEBHOOK is sent only for orders created through the API. It does not send 71 (Cancelling) or 73 (Refunding), and orders rejected in review or risk review are sent as 70. Events can arrive more than once and out of order, and intermediate statuses can be skipped. Deduplicate on orderId + orderStatus, and read the order with GET /v2/order/detail when an event does not fit the state you hold.
Handle failures
failReason is often empty, because many rails return no reason. Treat an empty failReason as “unknown”, not as success. To avoid paying twice, only create a replacement payout after the original order is final (70 or 72).
Reconcile
- Each payout: Get Order by
orderIdorclientIdis the source of truth:orderStatus,cryptoAmount(debited from the wallet),fiatAmount(paid to the beneficiary),fiatFee,orderPrice,orderFeeDetail,traceNumber,failCodeandfailReason. - Daily totals: List Orders with
orderType=crypto_to_fiatand a time range. It also lists orders not created through the API. - Wallet: Get Crypto Deposit History for top-ups, and Get Crypto Balance for
freeandlocked.
40 or 50 beyond your normal payout time. Webhooks can be missed after three failed delivery attempts.
Test in the sandbox
- Use
https://api-test.velafi.com. Webhooks are not sent in the sandbox, so pollGET /v2/order/detail. - Add test USDT or USDC to a merchant wallet with the crypto deposit simulator. See Sandbox Testing Tools.
- Some payment method IDs differ in the sandbox, for example CBU/CVU (Momentum) is
18instead of63. See Payment Methods. - The simulators only produce successful deposits. You cannot simulate a bounced payout, so test your
70and72handling with recorded payloads.
Go-live checklist
- The wallet is funded before orders are created, or post-funded off-ramp is enabled and your system funds orders in
32beforeorderExpireTimestamp. - Beneficiaries are registered once, stored by
userPaymentId, and only used whenstatusis1. - Every create request sends a unique
clientId, and timeouts are resolved by looking up theclientIdbefore retrying. - The
ORDER_WEBHOOKendpoint verifies the signature, returns{"callbackStatus":"SUCCESS"}with HTTP2xx, and deduplicates onorderId+orderStatus. -
70and72(including72after60) release or reverse the payout on your side, and a replacement is only created after the original is final. - Orders stuck in
40or50are flagged and escalated with theorderId. - For USD and EUR, the CPN account is activated and CPN beneficiaries include the required compliance fields.