clientId, and no reconciliation job to catch what webhooks missed.
Credentials and authentication
- Keep sandbox and production credentials apart. Sandbox keys come from VelaFi and are used with
https://api-test.velafi.com. Production keys are created on the API Management page and are used withhttps://api.velafi.com. The two sets are not interchangeable. See Sandbox vs. Production Keys. - Add the public IP address of every production server that calls the API to the key’s IP whitelist (up to 5 addresses per key, no CIDR ranges). A key without a whitelist cannot get a token (
-1023), and calls from other addresses fail with-2015(token request) or-2019(API call). Update the list before you move servers or change egress IPs. - Keep the API key, API secret and tokens on your server. Never put them in browser or mobile code, logs, or source control.
- Cache the token and request a new one before its
expireTime(Unix milliseconds). If a call returns-1022, the token expired or is unknown: get a new token and send the call again. - Plan key rotation: create the new key, whitelist its IPs, switch over, then delete the old key. See Security Best Practices.
Webhooks
Webhooks do not fire in the sandbox, so production is the first place your handler receives real deliveries. Check it carefully.- Register a production webhook for each event type you use with Create Webhook (
POST /v2/webhook), using your production credentials. Each event type (ORDER_WEBHOOK,GLOBAL_PAYMENT_WEBHOOK,FUNDING_WEBHOOK,ON_RAMP_WITHDRAW_WEBHOOK,CRYPTO_WITHDRAW_WEBHOOK,STABLE_COIN_PAYMENT_WEBHOOK) needs its own registration. Events that happen while no active webhook exists for their type are not queued, so register before your first live transaction. - Store each webhook’s
publicKeyand verify the RSA-SHA256signatureheader against the raw request body bytes, before you parse or re-serialize the JSON. See Webhook Setup. - Acknowledge every delivery you accept with HTTP
2xxand exactly this JSON body. If signature verification fails, return{"callbackStatus":"FAIL"}so the delivery is retried.
- Respond fast. Verify the signature, store the event, return the acknowledgement, and do slow work (database updates, calls to other systems) asynchronously.
- Handle duplicates. The same event can arrive more than once. Make processing idempotent on the event type, the record ID (
orderId,txIdorwithdrawId) and the status. - Do not rely on order or on seeing every status. Deliveries can arrive out of order, and an intermediate status can be skipped when the record has already moved on. Treat a webhook as a signal: read the record’s current state with the matching query endpoint and act on that.
- Reconcile what webhooks miss. A failed delivery is tried 3 times in total (the second attempt about 5–10 minutes after the first, the third about 60–65 minutes after the second). After that the event is dropped, and the API has no redelivery endpoint. Run the reconciliation job described below.
- Test your handler’s response before go-live. Send a sample payload to your own endpoint and check that the raw response is exactly
{"callbackStatus":"SUCCESS"}with a2xxstatus.
Idempotency with clientId
Send a new, unique clientId on every call that creates something, store it before you send the request, and reuse the same value when you retry that request. VelaFi rejects a reused clientId instead of creating a second record. The duplicate response does not return the original record, so look it up.
A
clientId stays bound to its record permanently, including after the record is cancelled. Use a new value for a new operation.
Create Merchant has no clientId. Before you retry it, look for the merchant by email with List Merchants (status=-1).
Error handling
- Branch on the
codefield, not on the HTTP status. Errors use HTTP400(or500for-1001) with a negativecode(except30012). There is no401or429. See Error Codes. - Treat
-1001,-1006,-1007and30012on a call that creates or changes something as outcome unknown. The operation may have completed. Look it up byclientId(table above) and retry with the sameclientIdonly if nothing is there. A-1174on that retry means the first request went through. - Do not match on
msg. It can contain runtime values and placeholders. - Back off on
-1006from a rate-limited endpoint and on30012(too many concurrent requests). - For failed orders, read
failCodeandfailReasonfrom Get Order.failReasoncan be empty or contain the provider’s raw text, so log it and handle the order by its status.
Reconciliation
Webhooks are a notification channel, not your ledger. Run a scheduled job that reads VelaFi’s records for the last few hours or days and compares them with your own.- Re-check every record your system holds in a non-final state, so a missed webhook does not leave an order or payout pending on your side forever.
- Alert on records that stay in a non-final status longer than you expect for that rail.
- Reconcile balances daily: opening balance plus funding records should equal the closing balance.
Limits
- Check each merchant’s daily and monthly limits and usage (
dayFiatLimit,monthFiatLimit,monthCryptoLimitand the matching...Usedfields) with List Merchants, and per-channel limits (monthBuyLimit,monthSellLimit) with List Merchant Accounts. - Handle limit errors:
-5050and-5060(order below the minimum or above the maximum),-5029,-5030and-5081(monthly or daily limit exceeded). - Request higher limits before launch if your expected volume needs them. See Transaction Limits.
- Respect the rate limits on quote and order endpoints.
Monitoring
- Log every request and response with your
clientIdand VelaFi’s ID (orderId,txId,withdrawId), so you can trace any transaction. - Alert on token failures (
-1022,-1023,-2015,-2019). They usually mean an expired token, a disabled key or an IP that is not whitelisted. - Alert when webhook deliveries stop arriving or when your handler returns anything other than the acknowledgement.
- Alert on rising error rates, on outcome-unknown codes, and on orders or payouts stuck in non-final statuses.
- Watch the balances you pay from (fiat balance for payouts and prefunded orders, crypto balance for off-ramps) so payouts do not fail for lack of funds.
Per use case
Each use-case page ends with a checklist for its own flow. Complete the one for every flow you launch:Collect with virtual accounts
Virtual account per payer,
FUNDING_WEBHOOK handling, deposit matching by depositAlias.Pay out from your balance
Beneficiaries, balance and fee checks, payout status tracking.
Convert to stablecoins
Prefunded or pay-per-order on-ramp orders, quotes, expiry handling.
Pay out funded with stablecoins
Off-ramp orders, crypto balance, payout failures and refunds.
Onboard your customers
Merchant creation, verification status, channel activation.
Cross-border payments
Fiat-to-fiat orders with
GLOBAL_PAYMENT_WEBHOOK.