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.
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 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.
  2. You call GET /customers/{id} to read kyc.status and kyc.requirements.
  3. 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.
  4. Verification runs asynchronously, so you poll GET /customers/{id} until it reaches a terminal status or surfaces new requirements, then handle the result.

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.
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. 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.
Get a customer
Result

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.

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.
Get an upload URL
Upload the file
Confirm the upload
The upload URL is valid for 15 minutes.

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.

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.