Skip to main content
This page walks developers through end-to-end sandbox testing of the Virtual Account flow: SumSub shared KYC, the provider-hosted questionnaire step, quote creation, provisioning, and deposit simulation for Noah, Due Network, and Brale. For the product overview and production wiring, see the Virtual Account Integration Guide.
Sandbox limitation. Provider sandboxes do not move real money.
  • Onramp can be completed end to end for Noah and Due Network via the deposit simulator. Brale has no sandbox deposit simulator, so there you stop at creating the virtual bank account.
  • Offramp only reaches a deposit address. There is no way to settle an offramp in sandbox.
Set these once and reuse them in every call below:

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.
  • kycShareProviders triggers 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, the POST returns 409 — retry the same call as a PATCH to re-issue.
  • mode can also be TOKEN_IMPORT (customer already has a SumSub applicant or token) or, via the multipart variant, RAW_DATA_IMPORT.
  • For HOSTED_URL mode, the response includes a hosted SumSub url. Send the customer there to complete identity verification.
Check status:
Look at 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)

Neither Noah nor Due is pre-filled by Meld. Meld only passes the SumSub verification result over (plus some standard agreement acceptances for Noah). It never submits real questionnaire answers on the customer’s behalf. Due’s own KYC-share request even has dedicated fields for prefilled answers, and Meld always sends them empty.For both providers, a human has to open a provider-hosted page and fill it in themselves. There is no way to skip this step programmatically.
How to tell if it’s still outstanding. On the same GET /accounts/customers/{customerId} call above, look at that provider’s entry in serviceProviderCustomers[]:
The questionnaire is still outstanding if 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:
Then 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.
Known gotcha (Due). Due also runs its own separate KYC/KYB approval independent of this questionnaire. A customer can complete the hosted page and still be rejected by Due at order time (err_kyc_not_passed) if Due’s own account import hasn’t completed on their side. If it persists, contact Meld to check.

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.
Onramp (buy) 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.
Onramp (buy)

Create a quote

This is the shared headless quote endpoint, not a virtual-account-only one. Always include paymentMethodType, or Meld falls back to Uphold’s card quote instead of Noah’s or Due’s bank quotes.
Swap in the provider’s test values from the tables above for Due (EUR / USDT_TRON / SEPA) or Brale (USD / USDC / LOCAL_BANK_TRANSFER). Note: Brale returns a fixed 1:1 rate.

Provision the virtual account

Returns 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 with POST /payments/virtual-account/deposit-instructions, and the provider’s normal webhooks settle it exactly as in production.
Sandbox only. In production this endpoint returns 404. Onramp (payin) only. There is no offramp equivalent; an offramp cannot be settled in sandbox.
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.
For Due Network, swap in "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 the TRANSACTION_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}
Abridged (a transaction carries many more fields — see Payins → Fetch the full transaction for the full shape):

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/currencies with type=CRYPTO, the partner name, and the relevant category to see what is available in sandbox.
  • Idempotency: include X-Idempotency-Key on order and provisioning requests so you can safely retry without creating duplicates.
  • Webhooks: TRANSACTION_CRYPTO_PENDING and friends may or may not fire in sandbox depending on the provider. If yours do not arrive, poll GET /payments/transactions instead — see Simulate a deposit.