Skip to main content
The auth frame is the lowest-friction way to obtain a fully-scoped access token. The customer verifies an email or SMS one-time passcode and, on success, the frame returns encrypted authenticated credentials that your client can use to call the MoonPay API on the customer’s behalf — for example to submit KYC information, fetch transactions, or perform other account-level operations.

How it works

The auth frame should only be launched after the check frame returns connectionRequired. The customer’s email address and optionally phone number (passed when you create a session) is how MoonPay identifies the user and where the one-time passcode is delivered:
  • If MoonPay has a phone number on file for that user, the customer can choose to receive the code via SMS instead. If not, email is the only option.
  • The customer does not need to be a returning MoonPay user. If no MoonPay account exists for the email yet, one is created during the OTP step. You can then drive any required onboarding or verification through other frames or directly via the API with the returned access token.

Handling email mismatches

Make sure the email you pass when creating the session matches the customer’s MoonPay account. If you send the wrong email — for example, a fresh address for a customer who already has an account under a different email — the auth flow will provision a brand-new account against the address you provided. The customer can later reverify with the correct email, but the duplicate account will be flagged. If a duplicate is detected, the customer will later be asked to resolve it through a challenge flow during verification. Use the full connect frame instead when you want MoonPay to orchestrate onboarding (KYC, address, document collection) inside the same flow.

URL

Requirements

Key exchange

Credentials returned from the frame are encrypted to protect their content since they are sent over postMessage. You need to generate an X25519 keypair and pass the public key into the frame. The frame uses your public key to encrypt the payload, ensuring only you can read it with your private key.
Never persist the private key to disk or storage. Hold it in memory only for the duration of the session.
Frame credential verification lifecycle
The frame uses the @noble/curves library internally. On web and React Native, you can use this same library to generate your keypair and handle decryption. For native platforms, use a compatible utility like CryptoKit on iOS or KeyPairGenerator on Android.
The following example shows how to generate a keypair and decrypt credentials using @noble/curves. You’ll want to add your own error handling and input validation for production use.
crypto.ts
The following example shows how to generate a keypair and decrypt credentials using @noble/curves. You’ll want to add your own error handling and input validation for production use.In React Native, yuo will need a polyfill for getRandomValues (MDN) which is only available in browsers.
crypto.ts

Co-branding

The frame is co-branded: it renders your account’s name and logo alongside MoonPay’s branding. Configure your name and logo in your MoonPay dashboard. You can’t override them with URL parameters.

Initialization parameters

Events

All events are dispatched using the message pattern described in the frames protocol. Below are the event payloads specific to the auth frame.

Outbound events

frame->parent These events are sent from this frame to the parent window.

handshake

The frame requests that you open a message channel.

ready

The frame has loaded and dispatched the customer’s first one-time passcode. Use this to reveal the frame and drop any loading state you were showing in its place.

complete

The auth flow finished. The payload is a Connection object whose status field tells you the outcome:
  • active — the customer is authenticated. The payload includes the customer’s id and encrypted authenticated client credentials. Any outstanding KYC or onboarding requirement is surfaced through capabilities, not a separate status — use the returned access token to drive it.
  • termsAcceptanceRequired — the customer must accept updated Terms of Use before the connection can be used. No credentials are attached: display the Terms of Use in your own UI, capture the acceptance timestamp, pass it as termsAcceptedAt when you create a new session (POST /platform/v1/sessions), then relaunch the flow. Passing termsAcceptedAt requires the Identity or Guest Checkout account capability.
  • pending / unavailable — status-only completions with no credentials: the connection exists but isn’t currently usable (a KYC decision is still pending, or the customer is in a restricted location).

error

This event dispatches errors that occur in the flow. The error message is developer-facing and not intended to be rendered in UI. The frame emits this event for every terminal error so the parent always knows when the flow has failed and can tear down the iframe or surface a fallback in your app. Recoverable mistakes the customer can retry inside the frame (such as an invalid or expired code) are not reported through this event — see Error handling for the full breakdown. Terminal errors that occur during the flow carry code: "generic". Invalid initialization parameters — a missing or malformed clientToken, publicKey, or channelId — are instead reported with code: "validationError" and a list of the offending fields, before the OTP flow starts.

close

The customer dismissed the frame. The parent should tear down the iframe or WebView in response.

Inbound events

parent->frame These events are sent from the parent window to this frame.

ack

Acknowledge the handshake.

Error handling

The auth frame splits failures into two categories. Inline errors are handled silently inside the frame so the customer can retry without the parent doing anything. Terminal errors end the flow — the frame shows a full-screen error state to the customer and sends an error post-message to the parent at the same time, so your app can react (for example, by tearing down the iframe or surfacing a fallback).

Inline errors

These are recoverable mistakes the customer can fix without leaving the OTP screen. The frame shows an inline message under the code input and stays open so the customer can retry. The parent does not receive an error event for these.

Terminal errors

These end the flow. The frame replaces its content with a full-screen error state — depending on the cause, the customer is offered a Retry button, a Close button, or both. The parent always receives an error event with code: "generic" for every terminal error, so it can dismiss the iframe or surface a fallback in your app.