Skip to main content
End-to-end guide for integrating Meld’s headless offramp (crypto → fiat) over the Payment and Account APIs.
This guide walks through customer creation, KYC, quoting, order creation, and webhook tracking for the headless offramp flow. Callers collect a provider deposit wallet address from Meld, have the user send crypto, and the provider pays out fiat to the bank / mobile money details you supplied.

Before you begin

  • You have a Meld API key with payment access (sandbox first, production for go-live)
  • You have a webhook endpoint configured in Developer → Webhooks in the dashboard
  • At least one headless-capable offramp provider is enabled on your account (for example Noah, Due Network, Brale, or Yellowcard)

How it works

  1. Create a customer in the Account API. (one time per user)
  2. Configure webhooks (including KYC events). (one time)
  3. Complete KYC for that customer with the offramp provider. (one time per user / provider)
  4. Get a sell quote from the Payment API. (every transaction)
  5. Create a headless offramp order with fiat payout details. (every transaction)
  6. The end user sends crypto to the returned walletAddress. (every transaction)
  7. Track progress via webhooks and the Transactions API. (every transaction)
Step 6 stays its own step: the user (or your treasury) must send the correct crypto amount to the provider-issued address before the fiat payout can complete.

1. Create a customer

Endpoint: POST /accounts/customers Once per end user. Reuse the same Meld customerId for all later KYC, quote, and order calls.

Required fields

Response:
Save the returned id — offramp create requires customerId.

Add a customer address (required for Due Network)

If you plan to use Due Network, add a residential address before KYC. Due uses address country when creating / sharing the provider customer. Endpoint: POST /accounts/customers/{customerId}/addresses
Response:
Optional grouping fields on the offramp order: subaccountCustomerId, externalSubaccountCustomerId.

2. Configure webhooks

Set this up before KYC so you receive CUSTOMER_KYC_STATUS_CHANGE as well as transaction events. In the Meld dashboard (Developer → Webhooks), subscribe at minimum to:
  • CUSTOMER_KYC_STATUS_CHANGE
  • TRANSACTION_CRYPTO_PENDING
  • TRANSACTION_CRYPTO_TRANSFERRING
  • TRANSACTION_CRYPTO_COMPLETE
  • TRANSACTION_CRYPTO_FAILED
Verify signatures per Webhook authentication docs.

3. KYC

Offramp providers that use Sumsub sharing (Noah, Due Network, and similar) follow the same ASYNC KYC pattern as virtual account onramp:
  1. Initiate Sumsub via POST /accounts/customers/{customerId}/kyc/initiate (once per customer)
  2. Set kycShareProviders so approval is shared to the offramp provider
  3. Wait for provider KYC APPROVED (webhook or GET /accounts/customers/{customerId}) before the first sell order for that provider

Initiate KYC

Endpoints:
  • POST /accounts/customers/{customerId}/kyc/initiate
  • PATCH /accounts/customers/{customerId}/kyc/initiate
Or import an existing Sumsub applicant:
Response:
Direct the user to url when present. Listen for CUSTOMER_KYC_STATUS_CHANGE. If the offramp provider needs extra KYC, serviceProviderCustomers[].kyc.additionalInfo.HostedURL (when present) is where you send the user next; wait until that provider’s status is APPROVED before creating sell orders.

4. Get a quote (sell)

Endpoint: POST /payments/crypto/quote?integrationMode=HEADLESS Header: Meld-Version: 2026-05-01 customerId is optional on quote (same as widget crypto quote). Pass it when available so ramp intelligence / previously-used scoring can use it.
Sell quotes with integrationMode=HEADLESS include CRYPTO_VIRTUAL_ACCOUNT_OFFRAMP providers (Noah, Due, and similar) as well as standard crypto offramp providers.
For a sell quote, sourceCurrencyCode is crypto and destinationCurrencyCode is fiat:
Response:
Capture the selected serviceProvider and amounts for order creation. Quote coverage varies by provider and rail. Headless rows include kycMode (ASYNC or SYNC).

5. Create a headless offramp order

Endpoint: POST /crypto/order/headless/offramp Auth: Payment charge permission on your API key / session. Idempotency (recommended): X-Idempotency-Key: <uuid>
  • Successful create → HTTP 201
  • Same key after success → cached body, HTTP 200
  • After a failed attempt that left an empty idempotency row → HTTP 425 TOO_EARLY — mint a new key

ACH / bank payout example

Address fields also accept snake_case aliases street_line_1 / street_line_2 for compatibility with older examples.

SEPA details shape

Mobile money details shape

Payout type options

Provider support for each type is account- and rail-specific.

Response


6. User sends crypto

Instruct the user (or your ops/treasury process) to send exactly the sourceAmount / currency from the order request to walletAddress. Incorrect amount, asset, or network can prevent payout. This is a required human (or treasury) action after order create — the order alone does not move crypto.

7. Track the transaction

Webhooks

Correlate using headlessOrderId (same as response id). For sell flows, transactionType is CRYPTO_SELL. Sample:

Fetch the full transaction

Endpoint: GET /payments/transactions/{paymentTransactionId} Use paymentTransactionId from the webhook. Example response:

Sequence Diagram

Sumsub KYC Approval Workflow 2026 07 18 004700

Notes and limits

  • Always wait for provider KYC approval before create when your provider requires shared Sumsub / onboarding.
  • Send the correct crypto amount and network/asset implied by your quote and order — the provider matches inbound crypto to the order.
  • Provider sandboxes may not complete full fiat settlement; you may stop at wallet address issuance.