Skip to main content
Use this flow to pay out local currency (MXN, ARS, COP, BRL, PEN) from a merchant’s fiat balance, also called the Pending Fund balance, to a bank account or Pix key you have saved as a beneficiary. You call POST /v2/merchant/claimfunds, VelaFi debits the balance and sends the payout on the local rail, and a FUNDING_WEBHOOK tells you whether it completed. The same balance can also be moved to another of your merchants with POST /v2/merchant/transfer. The balance usually comes from deposits. See Collect Local Payments with Virtual Accounts.

How it works

Before you start

Channels that support payouts from the balance

Three rules apply to every payout:
  1. Same channel. The payout goes out through the channel of the currency account that holds the balance. Only balance that came in through that channel (or through VelaFi Pending Fund) can be paid out on it.
  2. Same merchant currency account. The beneficiary must be saved under the same merchant, for the same currency and channel as that account. A beneficiary saved under another merchant, or for another channel (for example a Tesored beneficiary against a Finco Pay balance), is rejected.
  3. Amount plus fee. The balance must cover amount plus the payout fee.

Step by step

1

Save the beneficiary

Get the field template for the channel with GET /v2/payments/templates, then save the destination with POST /v2/payments under the merchant that holds the balance. See Manage Beneficiaries for templates per channel.
cURL
Keep the returned id: it is the userPaymentId for payouts. Pay out only to beneficiaries with status 1 (valid). If the account number is already saved, the response has status 3 and sameAccountNumberId; use that ID instead of creating a new record.
2

Check the balance

Read balance from GET /v2/merchant/accounts for the merchant, currency and channel. Leave room for the fee.
3

Create the payout

Call POST /v2/merchant/claimfunds. Always send your own unique clientId.
cURL
Response
amount is what the beneficiary receives. fee is the payout fee set for the channel and your account, and totalAmount (amount + fee) is debited from the balance when the call succeeds. Amounts are rounded down to the currency’s precision. A 200 response means the payout was accepted, not that it has arrived: it is pending until the webhook or the funding record says otherwise.
4

Wait for the result

Handle the FUNDING_WEBHOOK with type WITHDRAW for this txId (see Webhooks to handle). If you do not receive one, read the record with GET /v2/merchant/funding/records using txId or clientId.

Payout statuses

FUNDING_WEBHOOK is sent for 2, 3 and 4, not for pending.

Webhooks to handle

The payout event is a FUNDING_WEBHOOK with type WITHDRAW. totalAmount is negative for withdrawals.
FUNDING_WEBHOOK (WITHDRAW, canceled)
  • Match the event to your payout by clientId or txId.
  • Make the handler idempotent on eventType + txId + status. The same event can arrive more than once, and a payout can move from 2 to 4 later, so handle a refund after a completion.
  • failCode and failReason are sent only for canceled payouts that have a recorded reason. Many failures come without one; do not depend on them to decide what to do.
  • bankOrderId is the reference of the transfer at the payment provider, when available. Keep it for support requests.
  • Return HTTP 200 with {"callbackStatus":"SUCCESS"} quickly. VelaFi waits at most 60 seconds; a timeout or any other response counts as a failed delivery. See Delivery and Retries.
The full field list is in Funding Event.

Handle failures

Errors when creating the payout

If claimfunds returns an error, no funding record was created and nothing was debited. Common codes (see Error Codes):

Timeouts and retries

The clientId check makes retries safe as long as you reuse the same clientId:
  1. If the call times out or returns a network error, do not create a new clientId. Look up the payout with GET /v2/merchant/funding/records?clientId=....
  2. If a record exists, the payout was created: follow its status.
  3. If none exists, send the same request again with the same clientId. If the first request did go through, you get -1174 instead of a second payout.
A duplicate clientId returns the -1174 error, not the original payout. Read the original from the funding records.

Failed payouts

When a payout ends in status 3, the full totalAmount is back in the balance. A clientId cannot be reused, even for a failed payout, so to try again:
  1. Check the beneficiary details (account number, key type, holder name) and correct or re-save the beneficiary if needed.
  2. Create a new payout with a new clientId, for example payout-0001-r1, and link it to the original in your system.
Do not retry automatically in a loop. If the same beneficiary fails repeatedly without a failReason, contact VelaFi support with the txId and bankOrderId.

Move balance between merchants

To move balance from one of your merchants to another without a bank transfer, use POST /v2/merchant/transfer. Both merchants must belong to your account and have an active currency account for the same fiat and paymentId, on a channel that supports payouts.
1

Check the fee

Call GET /v2/merchant/transfer_fee with fiat. feeType 0 is a fixed amount and 1 is a fraction of the amount (0.01 = 1%). The fee returned by the transfer itself is the amount actually charged.
2

Create the transfer

cURL
The sender must have fiatAmount + fee available from that channel. The transfer completes in the same call: the response returns fromId (the TRANSFER_OUT record on the sender) and toId (the TRANSFER_IN record on the recipient), both with status 2. The recipient receives fiatAmount, and the sender is debited totalFiatAmount.
Transfers follow the same clientId rule as payouts: a clientId already used by any funding record under your account returns -1174. Always send one, so you can look the transfer up after a timeout. In the funding records, the TRANSFER_OUT record carries your clientId; the TRANSFER_IN record carries TRANSFER_IN_ followed by your clientId. Look both up by txId (fromId, toId).

Reconcile

  • GET /v2/merchant/funding/records is the source of truth for each payout and transfer. Filter by type=WITHDRAW (or TRANSFER_OUT, TRANSFER_IN), fiat and a time window, and check that every payout you created has a final status (2, 3 or 4). Investigate any that stay pending.
  • GET /v2/merchant/fiat/flows shows each balance movement: WITHDRAW when the payout is created, WITHDRAW_ROLLBACK when a canceled or refunded payout is credited back, and TRANSFER_OUT / TRANSFER_IN for transfers. The total on each line is the running balance after that movement.

Test in the sandbox

Use https://api-test.velafi.com with sandbox credentials. Fund a test merchant with the fiat deposit simulator, save a beneficiary, then call claimfunds and transfer against that balance. Use this to test your request validation, the error codes above and your clientId retry logic.
  • Webhooks do not fire in the sandbox. Follow each payout with List Funding Records, and test your webhook handler with sample payloads, including canceled (3) and refunded (4) withdrawals.
  • The simulators cannot produce a failed or returned deposit.

Go-live checklist

  • Every payout and transfer sends a unique clientId that you store before making the call.
  • On a timeout you look up the clientId before retrying, and you resend with the same clientId.
  • A failed payout is retried only with a new clientId, after the beneficiary details are checked.
  • Beneficiaries are saved under the same merchant and channel as the balance, and only status 1 beneficiaries are used.
  • The balance check includes the fee.
  • The FUNDING_WEBHOOK handler handles WITHDRAW with status 2, 3 and 4, and works when failReason is missing.
  • A scheduled job flags payouts still pending after your expected settlement time.
See also the general go-live checklist.