Skip to main content
Use this flow to collect local-currency payments (MXN, ARS, COP, BRL) from your own customers into a merchant’s fiat balance, also called the Pending Fund balance. You give each payer a virtual account with its own bank details, the payer sends a local transfer, and VelaFi credits the merchant’s balance and sends you a 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

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 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
Store the details against the payer. QR image links in 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 to FUNDING_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.
A delivery counts as successful only when your endpoint returns a 2xx status and the JSON body {"callbackStatus":"SUCCESS"}. Respond quickly: VelaFi waits at most 60 seconds, and a timeout counts as a failed delivery. A plain SUCCESS, {"received":true}, an empty body or your own error JSON all count as failures. After three failed attempts the event is not sent again, and the Open API has no redelivery endpoint. Acknowledge first, then process asynchronously. See Delivery and Retries.

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 and bankWebhookInfo.
  • Virtual account creation fails. status 3 means the channel rejected the account. -5079 means the account is not enabled for that currency and channel, and -5071 means the merchant’s currency account on that channel is not active. Check the merchant account before retrying.
  • COP alias rejected. For 68 and 69, 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/records with type=DEPOSIT, a fiat, and a startTime/endTime window (the window applies to the completion time). Compare every record with status 2 against the webhooks you stored, and process any you missed. For deposits, webhookInfo holds the same provider details as bankWebhookInfo, including depositAlias.
  • GET /v2/merchant/fiat/flows for the line-by-line balance history: each line has the change (changed), the running balance (total) and the related txId. Use it to prove that the balance in List Merchant Accounts equals the sum of your movements.
Funding records return 100 records per page by default (up to 20,000 with pageSize), and fiat flows up to 1,000 per page. Page through with currentPage.

Test in the sandbox

Use https://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_WEBHOOK endpoint verifies the signature and returns HTTP 200 with {"callbackStatus":"SUCCESS"} quickly, well within the 60-second timeout.
  • The handler is idempotent on eventType + txId + status and 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 depositAlias go 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.
See also the general go-live checklist.