> ## Documentation Index
> Fetch the complete documentation index at: https://docs.velafi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Go-Live Checklist

> Check credentials, webhooks, idempotency, error handling, reconciliation, limits and monitoring before you move a VelaFi integration to production.

Work through this page before you switch any VelaFi integration from the sandbox to production. The shared sections apply to every integration. The last section links to the checklist on each use-case page.

Most production incidents we see come from three gaps: webhook endpoints that do not acknowledge deliveries in the required format, retries without a `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 with `https://api.velafi.com`. The two sets are not interchangeable. See [Sandbox vs. Production Keys](https://docs.velafi.com/api-reference/authentication.md#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](https://docs.velafi.com/api-reference/authentication.md#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](https://docs.velafi.com/api-reference/webhooks/create-webhook-endpoints.md) (`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 `publicKey`** and verify the RSA-SHA256 `signature` header against the raw request body bytes, before you parse or re-serialize the JSON. See [Webhook Setup](https://docs.velafi.com/api-reference/webhooks/webhook-setup.md).
* **Acknowledge every delivery you accept with HTTP `2xx` and exactly this JSON body.** If signature verification fails, return `{"callbackStatus":"FAIL"}` so the delivery is retried.

```json theme={null}
{"callbackStatus":"SUCCESS"}
```

> [!WARNING]
>
> Anything else counts as a failed delivery and triggers a retry, even if your system processed the event. Each of these is a failure:
>
> * A plain-text `SUCCESS`, `success` or `OK`
> * `{"callbackStatus":"success"}` (the value is case-sensitive)
> * `{"received":true}`, `{"status":"ok"}` or any other JSON without `"callbackStatus":"SUCCESS"`
> * An empty body, or a body with whitespace or a byte-order mark before the opening `{`
> * Your own error JSON, or any non-`2xx` status
> * No response before the delivery times out

* **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`, `txId` or `withdrawId`) 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 a `2xx` status.

## 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.

| Call | `clientId` | Unique across | Reused `clientId` | Look up with |
| - | - | - | - | - |
| `POST /v2/order/fiat_to_crypto`, `POST /v2/order/crypto_to_fiat` | Optional, up to 40 characters | All `fiat_to_crypto` and `crypto_to_fiat` orders on your account | Rejected with `-1174` | [Get Order](https://docs.velafi.com/api-reference/order/retrieve-a-specific-order.md) with `orderType` and `clientId` |
| `POST /v2/order/fiat_to_fiat` | Optional, up to 40 characters | All `fiat_to_fiat` orders on your account | Rejected with `-1174` | [List Orders](https://docs.velafi.com/api-reference/order/retrieve-a-list-of-orders.md), match `clientId` |
| `POST /v2/merchant/claimfunds`, `POST /v2/merchant/transfer` | Send one on every request | All funding records on your account | Rejected with `-1174` | [Funding Records](https://docs.velafi.com/api-reference/merchant/retrieve-funding-records.md) with `clientId` |
| `POST /v2/assets/withdraw` | Required | Withdrawals of the same kind for that merchant | Rejected with `Duplicate withdrawal request` | [List Crypto Withdrawals](https://docs.velafi.com/api-reference/crypto-wallet/get-crypto-withdrawal-history.md) with `clientId` |
| `POST /v2/assets/internalTransfer` | Optional | Internal transfers sent by that merchant | Rejected with `Duplicate withdrawal request` | [List Crypto Transfers](https://docs.velafi.com/api-reference/merchant/query-crypto-transfers.md) with `clientId` |

A `clientId` stays bound to its record permanently, including after the record is cancelled. Use a new value for a new operation.

[Create Merchant](https://docs.velafi.com/api-reference/merchant/create-merchant.md) has no `clientId`. Before you retry it, look for the merchant by `email` with [List Merchants](https://docs.velafi.com/api-reference/merchant/get-a-list-of-merchants.md) (`status=-1`).

## Error handling

* Branch on the `code` field, not on the HTTP status. Errors use HTTP `400` (or `500` for `-1001`) with a negative `code` (except `30012`). There is no `401` or `429`. See [Error Codes](https://docs.velafi.com/docs/error-codes.md).
* Treat `-1001`, `-1006`, `-1007` and `30012` on a call that creates or changes something as **outcome unknown**. The operation may have completed. Look it up by `clientId` (table above) and retry with the same `clientId` only if nothing is there. A `-1174` on that retry means the first request went through.
* Do not match on `msg`. It can contain runtime values and placeholders.
* Back off on `-1006` from a [rate-limited endpoint](https://docs.velafi.com/api-reference/overview.md#rate-limits) and on `30012` (too many concurrent requests).
* For failed orders, read `failCode` and `failReason` from [Get Order](https://docs.velafi.com/api-reference/order/retrieve-a-specific-order.md). `failReason` can 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.

| What | Source of truth |
| - | - |
| Fiat deposits, payouts (`claimfunds`), on-ramp debits, refunds and merchant transfers on the fiat balance | [Funding Records](https://docs.velafi.com/api-reference/merchant/retrieve-funding-records.md) (`GET /v2/merchant/funding/records`), by `txId` or `clientId` |
| Line-by-line fiat balance changes, including fees and rollbacks | [Fiat Flows](https://docs.velafi.com/api-reference/merchant/get-a-list-merchant-fiat-flow.md) (`GET /v2/merchant/fiat/flows`) |
| On-ramp, off-ramp and global payment orders | [Get Order](https://docs.velafi.com/api-reference/order/retrieve-a-specific-order.md) and [List Orders](https://docs.velafi.com/api-reference/order/retrieve-a-list-of-orders.md) |
| Crypto withdrawals from merchant wallets | [List Crypto Withdrawals](https://docs.velafi.com/api-reference/crypto-wallet/get-crypto-withdrawal-history.md) |
| Crypto deposits | [List Crypto Deposits](https://docs.velafi.com/api-reference/crypto-wallet/get-crypto-deposit-history.md) |
| Fiat and crypto balances | [List Merchant Accounts](https://docs.velafi.com/api-reference/merchant/get-a-list-of-merchant-accounts.md) and [Get Crypto Balance](https://docs.velafi.com/api-reference/merchant/get-crypto-assets-balance.md) |

* 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`, `monthCryptoLimit` and the matching `...Used` fields) with [List Merchants](https://docs.velafi.com/api-reference/merchant/get-a-list-of-merchants.md), and per-channel limits (`monthBuyLimit`, `monthSellLimit`) with [List Merchant Accounts](https://docs.velafi.com/api-reference/merchant/get-a-list-of-merchant-accounts.md).
* Handle limit errors: `-5050` and `-5060` (order below the minimum or above the maximum), `-5029`, `-5030` and `-5081` (monthly or daily limit exceeded).
* Request higher limits before launch if your expected volume needs them. See [Transaction Limits](https://docs.velafi.com/docs/limits.md).
* Respect the [rate limits](https://docs.velafi.com/api-reference/overview.md#rate-limits) on quote and order endpoints.

## Monitoring

* Log every request and response with your `clientId` and 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:

> [card] [Collect with virtual accounts](https://docs.velafi.com/docs/use-cases/collect-with-virtual-accounts.md#go-live-checklist)
>
> Virtual account per payer, `FUNDING_WEBHOOK` handling, deposit matching by `depositAlias`.

> [card] [Pay out from your balance](https://docs.velafi.com/docs/use-cases/payouts-from-balance.md#go-live-checklist)
>
> Beneficiaries, balance and fee checks, payout status tracking.

> [card] [Convert to stablecoins](https://docs.velafi.com/docs/use-cases/convert-to-stablecoins.md#go-live-checklist)
>
> Prefunded or pay-per-order on-ramp orders, quotes, expiry handling.

> [card] [Pay out funded with stablecoins](https://docs.velafi.com/docs/use-cases/payouts-funded-by-stablecoins.md#go-live-checklist)
>
> Off-ramp orders, crypto balance, payout failures and refunds.

> [card] [Onboard your customers](https://docs.velafi.com/docs/use-cases/onboard-your-customers.md#go-live-checklist)
>
> Merchant creation, verification status, channel activation.

> [card] [Cross-border payments](https://docs.velafi.com/docs/guides/global-payments.md)
>
> Fiat-to-fiat orders with `GLOBAL_PAYMENT_WEBHOOK`.


## Related topics

- [Collect Local Payments with Virtual Accounts](https://docs.velafi.com/docs/use-cases/collect-with-virtual-accounts.md)
- [Pay Out Local Currency from the Balance](https://docs.velafi.com/docs/use-cases/payouts-from-balance.md)
- [Onboard Your Customers as Merchants](https://docs.velafi.com/docs/use-cases/onboard-your-customers.md)
- [Convert Local Currency to Stablecoins](https://docs.velafi.com/docs/use-cases/convert-to-stablecoins.md)
- [Pay Out Local Currency Funded by Stablecoins](https://docs.velafi.com/docs/use-cases/payouts-funded-by-stablecoins.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.