Skip to main content
A provider being able to take the order does not mean it will accept this customer. Check what is still outstanding before you create the order, so you collect it while the customer is still in the flow rather than surfacing a rejection once they think they are done.

Check the requirements

Endpoint: GET /crypto/onramp/{serviceProvider}/requirements Returns everything standing between this customer and an accepted order with that provider: agreements to sign, contacts to verify, and KYC still outstanding.
Response:

What comes back, and what to do

Four things, independent of each other. A customer can be through KYC and still be blocked on an unaccepted agreement. Work through them in the order below, then re-check.
Terms this provider wants the customer to see before it will take the order.
Render each url in your own UI and gate your own flow on the customer accepting. An empty array means there is nothing to show. Some providers, Coinbase for one, also need the acceptance on record. Then verificationRequirements.agreement lists the codes the customer must accept and how long an acceptance counts, and customerStatus.agreement shows what is unmet:
Read the codes from the response rather than hardcoding them. Record the acceptance in one of two ways:
  1. One receipt per code. Call POST /accounts/terms-of-service/acceptances for each code:
    ipAddress is the IP the customer accepted from, and it is required. Use the externalCustomerId of the customer you will order for: Meld creates a new customer for a value it has not seen. A customer without an externalCustomerId can’t hold receipts, and its agreement reads UNRECORDABLE.
  2. An acceptance time on the order. Send verification.agreementAcceptedAt on the create request: the ISO-8601 instant the customer accepted. Keep your own record of who accepted what, and when.
Meld reads receipts first and falls back to agreementAcceptedAt. An acceptance counts for maxAgeDays; after that, show the documents again.

Contact verification

Whether the provider wants the customer’s email or phone confirmed by a one-time code. Two keys work together: verificationRequirements is what the provider asks for, customerStatus is how far this customer has got.
satisfied is about verification, not about whether the value is on the customer. MISSING means never verified — the customer may well have an email on record. A customer with no value at all has to have one set on the customer before there is anything to verify. Verify only the channels that are required and not yet satisfied: POST /accounts/customers/{customerId}/verifications, then POST /accounts/customers/{customerId}/verifications/{verificationId}/confirm See Email and phone verification. When verificationRequirements.verifyWith names a serviceProvider, send that serviceProvider on the verification request. A code verified without it doesn’t count for that provider. STALE and VOIP both describe a customer who has verified before. A bare “verify your phone” reads as a bug to them unless you explain it. On VOIP. A phone number has a line type — mobile, landline, or a virtual number from an internet calling service. A provider that sets requireLineType: "MOBILE" will not accept the last two, because a number anyone can mint for free is weak evidence of who the customer is. The reason is named after the common case rather than the rule: it means “not the line type this provider requires”, so it would also cover a landline. It only fires when the line type is actually known. An unscreened number is left alone instead of being refused; otherwise a requireLineType policy would fail every customer. That makes VOIP rare in practice, and a provider that cares usually enforces its own rule on its side instead, so a number can still be refused later without this ever appearing. Expect MISSING and STALE.

KYC requirements

One entry per KYC step this provider needs, each reported independently. Collect everything in missingFields across all requirements in one pass, then send it as serviceProviderDetails on: PATCH /accounts/customers/{customerId}/kyc/initiate PATCH, not POST. The customer already has a KYC session by this point — POST starts one. This adds the provider’s own extra details to the session that exists. See Unified KYC for the modes and the payload.

Reading status

Two of these are easy to get wrong. PENDING is not failure. Treating it as REQUIRED sends the customer back through a form they have already filled in, and some providers review asynchronously, so it can last minutes. BLOCKED is not retryable. Re-submitting will not clear it. It is provider-specific, so the other providers in the quote are usually still open.

Provider processes

Some requirements are cleared by a process the provider runs, such as Coinbase’s limitUpgrade, which raises a customer’s limit. The requirement names the process, and the order is refused with 422 CUSTOMER_NOT_PAYMENT_READY until it completes. Run it from your backend, with the customer and the same order intent you checked: Endpoint: POST /crypto/onramp/{serviceProvider}/processes/{process}
READ returns the process status and the inputSources it accepts. Send operation: START with one of them as inputSource:
  • HOSTED_FORM returns hostedForm.url, a provider page for the customer to complete before hostedForm.expiresAt. Never log or store it.
  • CUSTOMER_KYC sends the provider details from the customer’s Unified KYC. It is refused with 403 KYC_NOT_COMPLETED if those details are missing.
Then READ every process.pollIntervalMs while pollable is true, and resend the order once status is SATISFIED. PENDING means the provider is still reviewing: stop polling after process.pollTimeoutMs and check eligibility later. BLOCKED and UNAVAILABLE mean the provider won’t proceed: offer another provider. Never retry START automatically.

Re-check before you create the order

Call the endpoint again with the same five order parameters. Submitting is not the same as satisfied: a provider may still be reviewing, and PENDING will not accept an order.