Quickstart
This is the 30 minute quickstart guide for executing the virtual account flow for the first time.
The virtual account flow is available white label, aka fully API driven, from Meld. The business can buy or sell digital assets in your UI, besides going to their banking app to send money. You will build your own UI on top of Meld's APIs for this product.
For Brale, this product is supported only for businesses, not individual users. A business will have to do contract with Meld and then submit KYC documents via a form link that Meld will provide. This product will support individual users in the near future.
In a buy transaction, the onramp will create a virtual bank account that the business will send money, and then the business will receive crypto to their wallet. In a sell transaction the business will send crypto to the onramp and then be paid in fiat from the virtual bank account to the business' own bank account.
A business will have the same virtual bank account each time they make a purchase with the same onramp.
This quickstart outlines both the buy and sell flow, however you should pick the one most relevant to your use case and execute it end to end first before moving onto the second flow.
For terminology, note that onramping, buying crypto, and payin mean the same thing. Similarly offramping, selling crypto, and payouts mean the same thing.
Integration Quickstart
Go through this flow to understand the key endpoints and make a successful transaction.
Time Required: 30 minutes
Difficulty: API knowledge required
Step 1: Get Your API Key
Your API key is required for all Meld API calls.
Process:
- Check your email for the Meld dashboard invitation
- Click the invitation link and log in
- In the dashboard, navigate to Developer > API Keys
- Click "Reveal Key"
- Copy and save your API key securely
Important: Always addBASICbefore your API key in all requestsExample:
BASIC W9kZTT7332okCEc1A9aqAq:3sYKoXQv6oHVHSts7G2agw9vTCXz
Step 2: Create and KYC a Customer
TODO:
- Add endpoint call to initiate kyc via Sumsub with the kycProviders set to Noah/Due.
- Wait for customer to get approved with Sumsub first, then the kyc provider used.
- Add note about patching kyc if additional requirements needed.
Step 3: Create an Order (Buy or Sell)
Note that in sandbox, you will not be able to complete a transaction, even a test one, so this is the last step you will get to. This endpoint needs to be called once per transaction.
Buy Flow Example (aka Payin)
API Call Details:
- Endpoint: Create Buy Order
- Method:
POST - Authorization:
BASIC [your-api-key]
Request Body:
{
"customerId": "WeP9eoFziQX4yXE5iiGqJE",
"sourceAmount": 100.00,
"sourceCurrencyCode": "USD",
"destinationCurrencyCode": "USDC",
"destinationWalletAddress": "0xC38dA09A61d2686aB64BF119Da37110Ecd089efE",
"paymentMethodType": "LOCAL_BANK_TRANSFER",
"serviceProvider": "BRALE"
}
Note: Replace thewalletAddresswith your own
Expected Response:
✅ 200 status code with the virtual bank account information to send money to.
{
"orderId": "WeP9eoFziQX4yXE5abcfec",
"paymentMethodType": "LOCAL_BANK_TRANSFER",
"receivingBankInformation": { // the VBA the onramp created for the business to send money to
"accountNumber": "12345678",
"routingNumber": "987654321"
},
"serviceProviderDetails": {
<raw data from the onramp>
}
}This is as far as you can get in sandbox. You cannot send money to the virtual account or to complete the transaction. However in production you will be able to do so, and this is how the flow continues.
Sell Flow Example (aka Payout)
API Call Details:
- Endpoint: Create Sell Order
- Method:
POST - Authorization:
BASIC [your-api-key]
Request Body:
{
"customerId": "WeP9eoFziQX4yXE5iiGqJE",
"sourceAmount": 100.00,
"sourceCurrencyCode": "USDC",
"destinationCurrencyCode": "USD",
"sourceWalletAddress": "0xC38dA09A61d2686aB64BF119Da37110Ecd089efE", // aka the business' wallet
"paymentMethod": {
"type": "LOCAL_BANK_TRANSFER",
"details": { // the business' information
"owner": "John Doe",
"name": "Chase Bank",
"accountType": "CHECKING",
"routingNumber": "021000021",
"accountNumber": "123456789",
"beneficiaryAddress": {
"street_line_1": "123 Main St",
"street_line_2": "Apt 4B",
"city": "New York",
"state": "NY",
"zip": "10001"
},
"bankAddress": {
"street_line_1": "270 Park Ave",
"street_line_2": "Ste 48",
"city": "New York",
"state": "NY",
"zip": "10017"
}
}
},
"serviceProvider": "BRALE"
}Expected Response:
✅ 200 status code with the onramp's wallet address to send crypto to.
{
"orderId": "WePFb16J4KjrRar71hbmtG",
"paymentMethodType": "LOCAL_BANK_TRANSFER",
"walletAddress": "0xC38dA09A61d2686aB64BF119Da37110Ecd089efE" // the onramp's wallet address
}This is as far as you can get in sandbox. You cannot send crypto to the onramp's wallet to complete the transaction. However in production you will be able to do so, and this is how the flow continues.
Save the orderId to use to fetch the transaction details.
Step 4: Complete the Transaction:
For onramp, go to your bank app and initiate a transfer of the amount to the bank information of the virtual bank account, and hit send. Note that the transfer time depends on your bank and the bank transfer rails in your country.
For offramp, go to your wallet and initiate a transfer of the token amount to the onramp's wallet address, and hit send. Note that the transfer time depends on which chain the token is on, but should be in the order of minutes.
Step 5: Verify Your Transaction
Fetch Transaction Details
Call the search transactions endpoint using the orderId from the previous step to view your transaction details.
Buy Flow Example (aka Payin)
Expected Response:
✅ 200 status code with transaction details
{
"transaction": {
"id": "WePZCYJW7cdXR7SxUMp8mE",
"parentPaymentTransactionId": null,
"accountId": "WQ5RyhdFzE45qjsomdzQ1u",
"isPassthrough": false,
"passthroughReference": null,
"isImported": false,
"customer": {
"id": "WePZCYZjAK97cJWokfH3Cc",
"accountId": "WQ5RyhdFzE45qjsomdzQ1u",
"externalId": "YC100"
},
"transactionType": "CRYPTO_PURCHASE",
"status": "SETTLED",
"sourceAmount": 100.00,
"sourceCurrencyCode": "USD",
"destinationAmount": 98.50,
"destinationCurrencyCode": "USDC",
"paymentMethodType": "LOCAL_BANK_TRANSFER",
"serviceProvider": "BRALE",
"serviceTransactionId": "1f0d470c-f5c2-6fc2-a54d-b6cdd6b6f014",
"orderId": null,
"description": null,
"externalReferenceId": "testSession",
"serviceProviderDetails": {
**raw data from the onramp**
},
"multiFactorAuthorizationStatus": null,
"createdAt": "2025-12-08T20:03:07.173223Z",
"updatedAt": "2025-12-08T20:08:08.877106Z",
"countryCode": "US",
"sessionId": "WePZCbr96gAUxgu7Auvm4q",
"externalSessionId": "testSession",
"paymentDetails": null,
"externalCustomerId": "testCustomer",
"fiatAmountInUsd": 100.00,
"sessionClientTags": null,
"serviceProviderTransactionUrl": null,
"serviceProviderCreatedAt": "2025-12-08T20:02:22Z",
"cryptoDetails": {
"sourceWalletAddress": null,
"destinationWalletAddress": "0xd72cc3468979360e31bc83b84f0887deccfd81d5",
"sessionWalletAddress": "0xd72cc3468979360e31bc83b84f0887deccfd81d5",
"totalFee": 1.50,
"networkFee": 0.10,
"partnerFee": 0.40,
"transactionFee": 1.00,
"totalFeeInUsd": 1.50,
"networkFeeInUsd": 0.10,
"partnerFeeInUsd": 0.40,
"transactionFeeInUsd": 1.00,
"blockchainTransactionId": "0x553d295955a978ed3e9fc1717b5bcb903c69577e49c8ad255abece945ffa9ba0",
"institution": null,
"chainId": "1"
}
}
}Sell Flow Example (aka Payout)
Expected Response:
✅ 200 status code with transaction details
{
"transaction": {
"id": "WePZCYJW7cdXR7SxUMp8mE",
"parentPaymentTransactionId": null,
"accountId": "WQ5RyhdFzE45qjsomdzQ1u",
"isPassthrough": false,
"passthroughReference": null,
"isImported": false,
"customer": {
"id": "WePZCYZjAK97cJWokfH3Cc",
"accountId": "WQ5RyhdFzE45qjsomdzQ1u",
"externalId": "YC100"
},
"transactionType": "CRYPTO_SELL",
"status": "SETTLED",
"sourceAmount": 100.00,
"sourceCurrencyCode": "USDC",
"destinationAmount": 98.50,
"destinationCurrencyCode": "USD",
"paymentMethodType": "LOCAL_BANK_TRANSFER",
"serviceProvider": "BRALE",
"serviceTransactionId": "1f0d470c-f5c2-6fc2-a54d-b6cdd6b6f015",
"orderId": null,
"description": null,
"externalReferenceId": "testSession",
"serviceProviderDetails": {
**raw data from the onramp**
},
"multiFactorAuthorizationStatus": null,
"createdAt": "2025-12-08T20:03:07.173223Z",
"updatedAt": "2025-12-08T20:08:08.877106Z",
"countryCode": "US",
"sessionId": "WePZCbr96gAUxgu7Auvm4q",
"externalSessionId": "testSession",
"paymentDetails": null,
"externalCustomerId": "testCustomer",
"fiatAmountInUsd": 100.00,
"sessionClientTags": null,
"serviceProviderTransactionUrl": null,
"serviceProviderCreatedAt": "2025-12-08T20:02:22Z",
"cryptoDetails": {
"sourceWalletAddress": null,
"destinationWalletAddress": "0xd72cc3468979360e31bc83b84f0887deccfd81d5",
"sessionWalletAddress": "0xd72cc3468979360e31bc83b84f0887deccfd81d5",
"totalFee": 1.50,
"networkFee": 0.10,
"partnerFee": 0.40,
"transactionFee": 1.00,
"totalFeeInUsd": 1.50,
"networkFeeInUsd": 0.10,
"partnerFeeInUsd": 0.40,
"transactionFeeInUsd": 1.00,
"blockchainTransactionId": "0x553d295955a978ed3e9fc1717b5bcb903c69577e49c8ad255abece945ffa9ba0",
"institution": null,
"chainId": "1"
}
}
}Dashboard Verification:
- Navigate to the Transactions tab
- If your transaction isn't visible:
- Click the Status dropdown
- Select "Select All"
- Your transaction should appear
✅ White-Label API Integration Complete! You can now build custom crypto experiences.
➡️ Build Custom UI Guide - Complete implementation guide
Next Steps
If you're ready to begin your integration, check out the end to end integration guide for the Virtual Account flow.
Troubleshooting
Common Issues and Solutions:
🚫 401 Unauthorized
- Ensure
BASICis added before your API key - Check for extra spaces or incorrect formatting
🚫 Webhook Not Received
- Verify URL is publicly accessible
- Check firewall settings
- Ensure webhook endpoint returns 200 status
🚫 Transaction Not Visible
- Change status filter to "Select All" in dashboard
- Wait 30 seconds and refresh
- Check if using correct environment (sandbox vs production)
Updated about 12 hours ago