Before you start
1
Create a customer
Create a
customerId via POST /accounts/customers (see Step 1 of the integration guide). If you need a business customer for KYB testing, contact Meld.2
Complete KYC
Complete SumSub shared KYC (below), then the provider-hosted questionnaire (below) for each provider you plan to use. Wait for the provider’s KYC to reach
APPROVED, not just SumSub’s.3
Enable whitelabel mode on your account
Ask Meld support to enable whitelabel mode (
headlessOnrampsEnabled). It is account-wide, not per-provider, but without it every virtual account call fails with 403 WHITELABEL_NOT_ENABLED even for a fully KYC-approved customer.4
Check supported currencies and rails
Call
GET /network-partner/supported/currencies with type=CRYPTO, partner=, and category=CRYPTO_VIRTUAL_ACCOUNT_ONRAMP (or CRYPTO_VIRTUAL_ACCOUNT_OFFRAMP) to see what’s available in sandbox for your target provider.SumSub shared KYC
One SumSub applicant approval gets reused across providers. The customer verifies identity once, and Meld shares the result to each provider you name. For the full sandbox KYC walkthrough, see KYC testing → Meld KYCes the user.kycShareProviderstriggers the fan-out. You can also share one provider at a time and repeat the call for the other; either works. If a provider was already shared, thePOSTreturns409— retry the same call as aPATCHto re-issue.modecan also beTOKEN_IMPORT(customer already has a SumSub applicant or token) or, via the multipart variant,RAW_DATA_IMPORT.- For
HOSTED_URLmode, the response includes a hosted SumSuburl. Send the customer there to complete identity verification.
serviceProviderCustomers[].kyc.status for SUMSUB and for each provider you shared to. You want the provider’s status at APPROVED, not just SumSub’s.
The provider-hosted questionnaire (Noah and Due)
How to tell if it’s still outstanding. On the sameGET /accounts/customers/{customerId} call above, look at that provider’s entry in serviceProviderCustomers[]:
kyc.additionalInfo.HostedURL is present and kyc.status is not APPROVED, REJECTED, or EXPIRED. There is no separate “questionnaire complete” flag; the presence of HostedURL plus the coarse KYC status is the only signal available. status moves to APPROVED or REJECTED asynchronously once the provider’s webhook fires after the customer finishes the hosted page. There is no intermediate “submitted, awaiting review” state.
If the link is stale (for example, Due’s hosted-session tokens expire), refresh it:
GET the customer again for the fresh HostedURL.
Sandbox note. The hosted step cannot be automated. If you are scripting sandbox tests, you need a pre-provisioned test customer who has already completed the real hosted questionnaire with each provider. There is no API-level bypass in a customer-facing environment.
Provider test values
Noah
Use the same values for onramp and offramp.Due Network
Due does not operate in the US. Use a non-US customer and EUR as the fiat currency.
Offramp (sell)
Brale
Brale does not support quotes. All exchanges are 1:1 between USD and USD-backed stablecoins. Brale also has no sandbox deposit simulator — see the simulator support table.
Create a quote
This is the shared headless quote endpoint, not a virtual-account-only one. Always includepaymentMethodType, or Meld falls back to Uphold’s card quote instead of Noah’s or Due’s bank quotes.
EUR / USDT_TRON / SEPA) or Brale (USD / USDC / LOCAL_BANK_TRANSFER). Note: Brale returns a fixed 1:1 rate.
Provision the virtual account
201 with bank or IBAN coordinates once provisioned, or 202 with a Retry-After header while the provider is still async-provisioning. Poll with the same X-Idempotency-Key.
If Due returns
403 for terms-not-accepted, re-sync KYC via the PATCH /kyc/initiate call above to obtain a fresh terms URL, rather than retrying the provisioning call blindly.Simulate a deposit
You cannot send real fiat to a sandbox virtual account, so there is nothing to trigger settlement. Call the endpoint below instead: Meld creates a test deposit into the standing account you fetched withPOST /payments/virtual-account/deposit-instructions, and the provider’s normal webhooks settle it exactly as in production.
Endpoint:
POST /payments/mock/headless/virtual-account/simulate-deposit
Repeat the provider, rail, currencies, and destination wallet from your deposit instructions, and set amount to the amount you want deposited.
"serviceProvider": "DUENETWORK", "paymentMethodType": "SEPA", "sourceCurrencyCode": "EUR", and a token like USDT_TRON. Leave network out entirely for both providers; it is a Uphold-only field.
Under the hood Meld dispatches per provider: Noah’s own sandbox simulator, Due’s sandbox payin. Either way the deposit settles asynchronously via webhook, not in the response.
Confirm settlement
Settlement is asynchronous, so the transaction does not exist yet when the simulate call returns. Normally you react to theTRANSACTION_CRYPTO_* webhooks you subscribed to in Payins. If you are scripting a test, or you have not set up a sandbox webhook endpoint, poll instead until a new transaction reads SETTLED. Typically 30 to 90 seconds for Noah, sometimes longer for Due.
Endpoint: GET /payments/transactions?customerIds={customerId}
Troubleshooting
Testing tips
- Wallet addresses: any valid address for the token’s chain. Some providers validate that the address format matches (for example, a TRON address for
USDT_TRON). - Start with the onramp (buy) flow. It is the most complete path in sandbox.
- Discover currencies and rails: call
GET /network-partner/supported/currencieswithtype=CRYPTO, the partner name, and the relevant category to see what is available in sandbox. - Idempotency: include
X-Idempotency-Keyon order and provisioning requests so you can safely retry without creating duplicates. - Webhooks:
TRANSACTION_CRYPTO_PENDINGand friends may or may not fire in sandbox depending on the provider. If yours do not arrive, pollGET /payments/transactionsinstead — see Simulate a deposit.