Before you begin
- You have completed the Virtual Account Quickstart end to end at least once in sandbox
- You have a Meld API key with virtual-account access (sandbox first, production for go-live)
- You have a webhook endpoint configured in Developer → Webhooks in the dashboard
- If you are KYCing end users, you have at least one virtual-account provider (Noah, Due, or Brale) enabled on your account
Notes
- This flow is supported for both businesses and individuals. Businesses will have to KYB with Meld, which is manual. Users will have to KYC via Meld with the onramp, which can be done via API, and is described below.
- While both Noah and Due support this flow for both businesses and users, Brale only supports this flow for businesses at this time. Brale also requires an additional redemption step in the onramp flow in order for the business to receive their crypto.
Full Integration Guide
This is the end-to-end guide for executing virtual account transactions (onramp and offramp) through the Meld API.
API compatibility: New fields may appear in responses without a version bump. Use flexible JSON parsing and do not reject unknown properties. Breaking changes ship under a dated Meld-Version.
How it works
- Create a customer in the Account API for the individual or business that will transact.
- KYB your business with Meld.
- If your end users will be making transactions, then KYC each customer with the provider (e.g. Noah, Due). Meld asks the provider to verify the customer; the provider responds asynchronously.
- Get a quote from the Payment API for the currency pair and amount.
- Create an onramp order (fiat to crypto) or offramp order (crypto to fiat).
- The business or individual completes the fiat or crypto leg (bank transfer or wallet send).
- Track progress via webhooks and the Transactions API.
1. Create a Customer
API Endpoint:POST /accounts/customers
Request:
id — you will pass it as customerId on every subsequent call.
If you plan on using
DUENETWORK later for executing transactions, you must also specify the customer’s address by calling POST /accounts/customers/{customerId}/addresses.Note: Order and transaction requests also accept optionalsubaccountCustomerIdandexternalSubaccountCustomerIdfields. Use these to tag which sub-account or business grouping a transaction belongs to for reporting and filtering purposes.
2. KYC / KYB
If you are transacting as a business, you will KYB with Meld. If your end users are transacting, each will have to KYC once with each onramp they use. KYC happens via Sumsub. If you have already KYCed your users with Sumsub, you can pass in the KYC token — see You KYC the user. If you’d prefer Meld to KYC the user, that process is described in Meld KYCes the user. To test KYC in the sandbox, follow the steps in KYC testing. Initiate KYC: API endpoints:POST /accounts/customers/{customerId}/kyc/initiatePATCH /accounts/customers/{customerId}/kyc/initiate
TOKEN_IMPORT mode).
To import the KYC of the user, call POST /accounts/customers/{customerId}/kyc/initiate. The customerId is in the path, and your request body looks like this:
url to complete verification. If you are not passing in a token, Sumsub will ask the user for all required fields, and if the token is passed in, Sumsub will only prompt the user for any missing fields. KYC completes asynchronously — the provider notifies Meld via webhook, and Meld publishes a CUSTOMER_KYC_STATUS_CHANGE webhook to your endpoint.
Sample KYC webhook payload:
GET /accounts/customers/{customerId}
Check serviceProviderCustomers[].kyc.status. Values: PENDING, APPROVED, REJECTED, EXPIRED, UNKNOWN.
Wait for the kyc.status to be APPROVED before proceeding. The customer must be KYC-approved before any transactions can be created.
In both KYC initiation modes (HOSTED_URL and TOKEN_IMPORT), you can optionally specify Virtual Account providers you want to share the user’s KYC with after Sumsub approves submission, via the kycShareProviders field. Alternatively, you can add share providers later by calling PATCH /accounts/customers/{customerId}/kyc/initiate.
CUSTOMER_KYC_STATUS_CHANGE webhooks.
Supported share providers and their requirements
- Noah
- Due Network. Requires customer country to be specified — call
POST /accounts/customers/{customerId}/addressesto add an address.
GET /accounts/customers for more details. For example, when the Virtual Account provider requires additional KYC on their platform, you will receive a webhook event with kycRecipient.status=PENDING. Then, when calling GET /accounts/customers, you will receive the following response:
HostedURL so they can pass additional KYC on the Virtual Account provider’s platform. After users complete additional KYC and the Virtual Account provider verifies it, you will receive another CUSTOMER_KYC_STATUS_CHANGE event with kycRecipient.status=APPROVED. The new status will also be reflected in GET /accounts/customers responses.
Errors:
- Calling
initiateagain for the same customer + provider returns 409.
3. Configure webhooks
In the Meld dashboard (Developer > Webhooks), add your endpoint URL and subscribe to at minimum:TRANSACTION_CRYPTO_PENDINGTRANSACTION_CRYPTO_TRANSFERRINGTRANSACTION_CRYPTO_COMPLETETRANSACTION_CRYPTO_FAILEDCUSTOMER_KYC_STATUS_CHANGE
4. Get a Quote
API Endpoint:POST /payments/virtual-account/crypto/quote
Note that the provider Brale doesn’t support quotes. All Brale transactions are 1:1, aka for $100 you will receive 100 USDC.
Request:
sourceCurrencyCode is the crypto, destinationCurrencyCode is the fiat.
Response (abbreviated):
| Field | Meaning |
|---|---|
sourceAmount | What the user spends (fiat for buy, crypto for sell) |
destinationAmount | What the user receives |
totalFee | Total fees in the fiat currency |
serviceProvider | Provider backing this quote |
transactionType | CRYPTO_PURCHASE (buy) or CRYPTO_SELL (sell) |
serviceProvider for the next step.
5. Create an Order
Buy (onramp) — fiat to crypto
API Endpoint:POST /payments/virtual-account/ramp/onramp/order
Use the customerId from Step 1. The serviceProvider comes from the quote the user selected.
Request:
destinationWalletAddress after receiving the funds.
Sell (offramp) — crypto to fiat
API Endpoint:POST /payments/virtual-account/ramp/offramp/order
Request:
paymentMethod.type options:
| Type | Details schema |
|---|---|
ACH / LOCAL_BANK_TRANSFER | accountType, routingNumber, accountNumber, bankName, beneficiaryAddress, bankAddress |
SEPA | iban |
walletAddress from their wallet. The provider pays out fiat to the bank details after receiving the crypto. Make sure the user sends the correct amount of crypto as in the Create Sell Order API call.
6. Track the Transaction
Webhooks
Meld sends webhooks as the transaction progresses. Correlate to your order usingvirtualAccountRampOrderId in the payload (matches the orderId from the order response).
Find more information on the various webhook event types in Webhook events.
Sample webhook payload (buy)
transactionType is CRYPTO_SELL.
The paymentTransactionStatus field shows the current status of the transaction. Find the list of transaction statuses and their meanings in Transaction statuses.
Fetch the full transaction
ExtractpaymentTransactionId from the webhook and call:
API Endpoint: GET /payments/transactions/{paymentTransactionId}
Sample response:
orderId on the transaction matches virtualAccountRampOrderId in the webhook and the orderId from your order response, tying all three together.
Sequence diagram
Testing
Provider sandboxes often cannot complete full settlement — you may only reach virtual bank account creation. See Virtual account flow testing credentials for provider-specific sandbox values (Noah, Due, Brale).
Next steps
Testing your implementation
- Virtual account testing credentials — provider-specific sandbox values
Advanced features
- Webhook events — real-time updates
- Dashboard data — track performance
Production deployment
- Service-provider setup — configure additional onramps
Support & resources
- Postman collection — test APIs directly
- FAQ — common questions answered