FUNDING_WEBHOOK that names the virtual account the money arrived on. From that balance you can pay out, move funds to another merchant, or buy crypto.
How it works
One account or one per payer
A virtual account is for attribution only. It has no balance of its own: every deposit, on any virtual account, is credited to the single balance of the merchant’s currency account. Keep per-payer balances in your own ledger, keyed by
depositAlias.Before you start
- API access. An API key, your server IP on the whitelist, and a business token in the
X-BH-TOKENheader. See Authentication. - A verified merchant. See Create a Merchant.
- An active currency account for the currency and channel you want to collect on (status
2in List Merchant Accounts). See Activate a Merchant Account. - A
FUNDING_WEBHOOKendpoint registered and returning the required acknowledgement. See Webhooks and Webhook Setup.
Channels that support virtual accounts
The merchant must already have an active currency account on that channel. A currency account on a channel that does not support virtual accounts returns
-5084 (this currency does not support multiple virtual accounts). default, in any letter case, is reserved.
Step by step
1
Create a virtual account for each payer
Call
POST /v2/merchant/virtual/accounts with the merchant, currency, channel and an alias you choose. Use a stable ID from your system, such as your customer ID, so you can match deposits without a lookup table. The alias is permanent.cURL
Response
status is 1 (pending) while the channel sets up the account, 2 when it is active, and 3 if it failed. ARS (63) accounts return 2 at once. Sending the same alias again while it is pending returns -5085; once it is active, -5086.2
Get the bank details for the alias
Poll Store the details against the payer. QR image links in
GET /v2/merchant/virtual/addfunds with depositAlias until the record’s status is 2, then read its fieldList. The keys depend on the channel and are listed in fieldList keys by payment method.cURL
fieldList expire one hour after the response, so fetch a fresh one when you show it again. To fund the merchant’s main account instead, call GET /v2/merchant/addfunds.3
Show the payer what to do
Show the payer the details for their alias. What the payer does depends on the rail:
4
Handle the deposit webhook
When the deposit is credited, VelaFi creates a funding record of type
DEPOSIT with status 2 and sends a FUNDING_WEBHOOK. Verify the signature header, store the event, and return HTTP 200 with {"callbackStatus":"SUCCESS"}. Credit the payer identified by depositAlias in your ledger. See Webhooks to handle.5
Check the balance
Read the merchant’s balance from
GET /v2/merchant/accounts: the balance of the record for that currency and channel. This is the amount available for payouts, transfers and on-ramp orders.cURL
Webhooks to handle
Subscribe toFUNDING_WEBHOOK. One webhook receives events for every merchant under your account. A deposit event looks like this (all values are strings):
FUNDING_WEBHOOK (DEPOSIT)
The full field list is in Funding Event.
Handle failures
- Duplicate or out-of-order events. The same event can arrive more than once, and an event whose status has changed before delivery is dropped. Make the handler idempotent on
eventType+txId+status, and read the record from List Funding Records when you need its latest state. - Missed webhooks. If your endpoint was down or answered incorrectly, nothing is resent. Run the reconciliation below on a schedule so no deposit is missed.
- Deposit without a
depositAlias. Payments to the merchant’s main account details, or rails that do not report the alias, have no virtual account to match. Hold them for manual review using the payer fields andbankWebhookInfo. - Virtual account creation fails.
status3means the channel rejected the account.-5079means the account is not enabled for that currency and channel, and-5071means the merchant’s currency account on that channel is not active. Check the merchant account before retrying. - COP alias rejected. For
68and69, the alias must be 6 to 21 uppercase letters or digits.
Reconcile
The funding records are the source of truth for what was credited to the balance:GET /v2/merchant/funding/recordswithtype=DEPOSIT, afiat, and astartTime/endTimewindow (the window applies to the completion time). Compare every record with status2against the webhooks you stored, and process any you missed. For deposits,webhookInfoholds the same provider details asbankWebhookInfo, includingdepositAlias.GET /v2/merchant/fiat/flowsfor the line-by-line balance history: each line has the change (changed), the running balance (total) and the relatedtxId. Use it to prove that the balance in List Merchant Accounts equals the sum of your movements.
pageSize), and fiat flows up to 1,000 per page. Page through with currentPage.
Test in the sandbox
Usehttps://api-test.velafi.com with sandbox credentials. You can create and list virtual accounts there, and credit test deposits with the fiat deposit simulator (POST /v2/platform/test/deposit). Pass depositAlias to send the deposit to a virtual account.
- Webhooks do not fire in the sandbox. Test your handler by posting sample payloads to it yourself, and check the result of each simulated deposit with List Funding Records.
- The simulator only produces successful deposits. It cannot produce a failed or returned deposit.
Go-live checklist
- The
FUNDING_WEBHOOKendpoint verifies the signature and returns HTTP200with{"callbackStatus":"SUCCESS"}quickly, well within the 60-second timeout. - The handler is idempotent on
eventType+txId+statusand ignores events it has already processed. - Every payer has their own virtual account, and the alias maps to one customer in your system.
- Your ledger keeps per-payer balances; you do not expect VelaFi to hold a balance per virtual account.
- Deposits without a
depositAliasgo to a review queue. - A scheduled job compares funding records (
type=DEPOSIT) with stored webhooks and processes the gaps. - QR codes and payment links are fetched fresh before they are shown.