Skip to main content
Run KYC in your own UI. With the Customer API you own the verification screens: MoonPay tells you what’s required through kyc.status and kyc.requirements, you capture the data from the customer, and you submit it, keyed on customerId. It answers three questions, server-side or client-side: is this customer KYC’d, what’s missing, and how do you submit it. This is the API-driven onboarding path. If you’re deciding between this path, the hosted connect flow, and guest checkout, see Choose an onboarding path. See the Going Live section for requirements you must meet before taking this integration to production.

Prerequisites

  • A server that can create session tokens with your secret key.
  • A client that can render the Auth frame, with the SDK or manually.
  • Either a secret key or an access token for API calls. Secret-key calls are scoped to your customers; access-token calls are scoped to the token’s own customer.
  • A UI surface that presents MoonPay’s Terms of Use and Privacy Policy and records the customer’s acceptance. See Terms acceptance.
Customers who arrive through the hosted connect flow also work with this API. You need a customer id from an authenticated connection, however that connection was established.

How it works

  1. You present the terms: show MoonPay’s Terms of Use and Privacy Policy in your UI and capture the acceptance timestamp.
  2. You authenticate the customer: create a session, check the connection with skipKyc: true, and render the Auth frame when the customer isn’t connected yet. This gives you the customer’s id.
  3. You call GET /customers/{id} to read kyc.status and kyc.requirements.
  4. You submit outstanding requirements with PATCH /customers/{id}/kyc, and upload any required files. Once every requirement is submitted, verification starts automatically. If MoonPay can’t complete verification from the submitted data alone, the response includes a hosted challenge to render.
  5. Verification runs asynchronously, so you poll GET /customers/{id} until it reaches a terminal status or surfaces new requirements, then handle the result.

Present the terms

Before a customer’s first transaction, present MoonPay’s Terms of Use and Privacy Policy in your UI using one of the two presentation methods, and capture the timestamp at the moment the customer explicitly accepts. You pass that timestamp as termsAcceptedAt when you create the session in the next step. For the presentation methods, the rendering rules, and what the recorded acceptance covers, see Terms acceptance.

Authenticate the customer

Every Customer API call is keyed on a customer id from an authenticated connection. To establish one without the hosted connect flow, create a session, check the connection, and render the Auth frame. On your server, create a session token with your secret key and send it to your client. Include the customer’s email when you create the session: the Auth frame identifies the customer by that address and delivers the one-time passcode there. If no MoonPay account exists for the email yet, the OTP step creates one.
Include the termsAcceptedAt timestamp you captured when you presented the terms — see Record the acceptance for the field’s rules. On your client, check for an existing connection with client.getConnection({ skipKyc: true }). The skipKyc option opts the check out of KYC-based statuses, so an unverified customer isn’t blocked with pending or failed before you submit their data. The check still surfaces legal statuses: termsAcceptanceRequired appears when a Terms of Use attestation is outstanding — see Terms acceptance for how to present the terms and record acceptance. When the status is connectionRequired, render the Auth frame with client.setupAuth(). The Auth frame handles authentication only, with no payment-method setup or KYC steps. On the complete event with status: "active", you have the customer’s id and subsequent SDK calls are authenticated automatically.
Authenticate the customer
If the connection check returns status: "active", the customer is already authenticated: read customer.id from the result and skip the Auth frame. For the other statuses, including termsAcceptanceRequired, see client.getConnection().
The connection check primes the client with the token the Auth frame needs. If you call setupAuth() without a prior connection check that resolved with status: "connectionRequired", it returns a configurationError. See client.setupAuth() for the full event and error reference.

Get a customer

Returns the customer’s KYC standing and outstanding requirements. For request and response details, see the Get a customer API.
Get a customer
Result
When a requirement is incomplete, requiredFields lists the outstanding fields the customer must provide. Only basicDetails, residentialAddress, taxIdentifiers, and questionnaires populate it; it’s omitted when the requirement is complete, and the other requirement categories don’t surface field-level detail. For questionnaires, the values are the outstanding questionnaire types — see Due diligence questionnaires.

Submit KYC data

Submit one or more outstanding requirement categories. Only send the categories listed as incomplete in kyc.requirements. For the fields and documents each country requires, see KYC data requirements.
Submit KYC data
Returns the updated customer, so you can re-check kyc.requirements for what’s still outstanding. For request and response details, see the Submit KYC data API. You submit due-diligence questionnaires through this same endpoint. When questionnaires is incomplete in kyc.requirements, its requiredFields names the questionnaires to complete: customerDueDiligence, enhancedDueDiligence, or both. Submit each one as an entry in questionnaires[], with a type and an answers object. Monetary answers (grossAnnualIncome, expectedTransactionAmountPerMonth, and netWorth) take a currency and an amount. Denominate each amount in the reporting currency for the customer’s residentialAddress.country; the API rejects any other currency with a 400 validation error that identifies the expected one. If the customer’s local currency differs from the reporting currency, convert the amount before submitting, using a current exchange rate. For the country-to-currency mapping and the full questionnaire field reference, see Due diligence questionnaires.
Submit questionnaire answers

Upload a file

For document-based requirements (for example, identityDocuments, selfie, or proofOfAddress), first get a presigned upload URL, PUT the file to it, then confirm the upload.
Before you submit selfie or identity-document images, display the required biometric consent disclosure to the customer. See Biometric consent for selfie and document images.
Get an upload URL
Upload the file
Confirm the upload
The upload URL is valid for 15 minutes. For request and response details, see the Get a file upload URL and Confirm uploaded files APIs.

Poll for the outcome

Submitting the last outstanding requirement starts verification automatically, and it runs asynchronously. Poll GET /customers/{id} while kyc.status is verifying. Once it changes, verification has finished: a terminal value (active or unavailable) is a final outcome, and collecting means MoonPay needs more information from the customer. Submit the newly outstanding requirements with PATCH /customers/{id}/kyc to restart verification.
Poll for the outcome

Handle the hosted challenge

When MoonPay can’t complete verification from the submitted data alone, kyc.challenge is present on the PATCH /customers/{id}/kyc response ({ url, expiresAt }). Render this URL for the customer to finish verification; do not resubmit requirements. The challenge can also appear on a later GET /customers/{id} while it’s still outstanding, so check for it whenever you re-fetch the customer. See Handle challenges for the full flow.
Error codes include verification_rejected on a terminal rejection, and country_mismatch when submitted data conflicts with the declared country.

KYC status

Next steps

Export customer data

Share a customer’s verified KYC data with another system, with their consent.

Handle challenges

Render the Challenge frame when verification needs extra steps.

Terms acceptance

Present the terms, record acceptance, and cover the related consent and verification steps before going live.

Choose a payment method

Compare the payment surfaces you can build once the customer is verified.

API and SDK credentials

Understand the secret keys and access tokens this API accepts.