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.
id from an authenticated connection, however that connection was established.
How it works
- 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’sid. - You call
GET /customers/{id}to readkyc.statusandkyc.requirements. - 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. - 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 customerid 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.
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
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 asincomplete in kyc.requirements. For the fields and documents each country requires, see KYC data requirements.
Submit KYC data
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
Poll for the outcome
Submitting the last outstanding requirement starts verification automatically, and it runs asynchronously. PollGET /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.