What you can and cannot test in sandbox
Provider sandboxes do not move real money, so not every path can be completed. Read this before you start, so you know where each flow is expected to stop.The steps below use Noah, which reaches a settled transaction in the fewest moves. Due Network and Brale follow the same steps with the values in Provider test values.Each provider is enabled per account and carries its own KYC approval, so confirm both in Step 2 before you start. A provider whose KYC is not
APPROVED returns 403 KYC_NOT_COMPLETED.Prerequisites
1
A sandbox API key
From the Meld dashboard, Developer → API keys, in the sandbox environment. Sandbox and production keys are not interchangeable.
2
curl and jq
jq is only used to pull one id out of a response. On macOS it ships with the OS; on Debian or Ubuntu, apt install jq.3
Whitelabel mode enabled on your account
Ask Meld support to enable whitelabel mode (
headlessOnrampsEnabled). It is account-wide rather than per-provider, and without it every virtual account call fails with 403 WHITELABEL_NOT_ENABLED, even for a fully KYC-approved customer.4
At least one virtual-account provider enabled
Noah is the one this guide uses. Ask Meld support to confirm it is enabled on your sandbox account.
Step 1 — Set up your shell
Paste your sandbox key into the first line. Everything after this is copy-paste.customerId and keep it in your shell:
WmYNx6LnmqasLfcp8HngoD. If you get null, the pre-created customer is not on your account — create your own using Using your own customer instead.
The pre-created customer lets you skip customer creation, SumSub KYC, and the provider-hosted questionnaire — the three steps that cannot be automated. That is the fastest route to a settled transaction. To exercise the full onboarding path yourself, see Using your own customer.
Step 2 — Confirm the customer is ready
200, with the provider you intend to use reading "kycStatus": "APPROVED":
APPROVED, not just SumSub’s — SumSub approval alone is not enough to transact.
A provider listed as PENDING has been shared but has not finished its own hosted questionnaire, and every call to it returns 403 KYC_NOT_COMPLETED until it does. Complete that provider’s questionnaire as described in Using your own customer. A provider missing from the list entirely is either not enabled on your account or has not been KYC-shared to this customer.
Step 3 — Get a quote
This is the shared headless quote endpoint, not a virtual-account-only one. Always includepaymentMethodType, or Meld falls back to a card quote instead of the bank quotes the virtual account flow needs.
200 with at least one quote. serviceProvider is the value you carry into the next step:
quotes array with NO_VALID_QUOTES means no provider serves that corridor and rail for your account — check your values against Provider test values.
This endpoint also accepts
externalCustomerId in place of customerId, which is convenient if you have not resolved the Meld id yet. The provisioning and simulation endpoints in Steps 4 and 5 do not — they take customerId only.Step 4 — Provision the virtual account
This returns the standing bank details your customer would send fiat to. Fetch it once per customer, provider, payment method, currency pair, and destination wallet — it is reusable, not per-transaction.201 with bank or IBAN coordinates.
202 with a Retry-After header means the provider is still provisioning asynchronously. Poll with the same X-Idempotency-Key until you get the 201.
Step 5 — Simulate a deposit
You cannot send real fiat to a sandbox virtual account, so nothing would trigger settlement. This endpoint creates a test deposit into the standing account from Step 4, and the provider’s normal webhooks settle it exactly as in production.
Repeat the provider, rail, currencies, and destination wallet from Step 4, and set
amount to the amount you want deposited.
200 with a provider reference:
"serviceProvider": "DUENETWORK", "paymentMethodType": "SEPA", "sourceCurrencyCode": "EUR", and a token such as USDT_TRON. Leave network out entirely for both providers; it is a Uphold-only field.
Step 6 — Confirm settlement
Settlement is asynchronous, so the transaction does not exist yet when Step 5 returns. In a real integration you react to theTRANSACTION_CRYPTO_* webhooks from Payins. For a scripted test, poll instead:
200. The newest transaction starts at PENDING and reaches SETTLED — typically 30 to 90 seconds for Noah, sometimes longer for Due.
status reads SETTLED. That is the end of the onramp flow.
A transaction carries many more fields than shown here — see Payins → Fetch the full transaction for the full shape.
Using your own customer
Skip this if you used the pre-created customer. Use it when you want to exercise onboarding yourself. Create a customer.200 and a base58 id. For a business customer for KYB testing, contact Meld.
Share SumSub KYC to the providers. One SumSub applicant approval is reused across providers — the customer verifies identity once and Meld shares the result to each provider you name. For the full walkthrough see KYC testing → Meld KYCes the user.
200 with a hosted SumSub url. Send the customer there to complete identity verification.
A 409 means that provider was already shared. Re-issue with PATCH instead — it takes the same body as the POST, so serviceProvider and mode are required alongside kycShareProviders, and omitting them returns 400:
mode can also be TOKEN_IMPORT when the customer already has a SumSub applicant or token, or RAW_DATA_IMPORT via the multipart variant.
Complete the provider-hosted questionnaire. After SumSub approval, a provider may still require its own step and sit at PENDING. The link lives on the customer record, 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, and no intermediate “submitted, awaiting review” state — status moves to APPROVED or REJECTED asynchronously once the provider’s webhook fires.
If a hosted link has gone stale — Due’s hosted-session tokens expire — re-issue the share with the PATCH above, then re-fetch the customer for the fresh HostedURL.
Due Network terms of service. Due maintains its own terms acceptance, separate from KYC, and a customer can be KYC-approved with terms still outstanding. Re-issuing the share with the
PATCH above refreshes it: Meld re-reads Due’s account and, while the terms are pending, writes the current link to serviceProviderCustomers[].kyc.additionalInfo.TosURL on the customer record.The link is not in the PATCH response — that returns the SumSub URL. Re-fetch GET /accounts/customers/{customerId} and read TosURL from there.If TosURL is absent after a re-issue, Due is reporting the terms as already accepted. If Due still rejects the order for terms, contact Meld.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, so the flow stops at Step 4.
Troubleshooting
Testing tips
- Wallet addresses: any valid address for the token’s chain. Some providers validate the format matches — a TRON address for
USDT_TRON, for example. - Start with the onramp (buy) flow. It is the only path that reaches
SETTLEDin sandbox. - Discover currencies and rails: call
GET /network-partner/supported/currencieswithtype=CRYPTO, the partner name, andcategory=CRYPTO_VIRTUAL_ACCOUNT_ONRAMP(orCRYPTO_VIRTUAL_ACCOUNT_OFFRAMP). - Idempotency: include
X-Idempotency-Keyon provisioning requests so you can safely retry without creating duplicates. - Partner discovery is not account enablement.
GET /network-partner/supported/partnerslists what the catalog supports, which can be more than what is enabled on your account.