Skip to main content

Documentation Index

Fetch the complete documentation index at: https://dev.moonpay.com/llms.txt

Use this file to discover all available pages before exploring further.

Frames adopt your brand at runtime. Pass a brand color and, optionally, a theme object encoded in the URL. The entire palette, border radii, and color scheme derive from these params at request time. WCAG AA contrast is guaranteed when you pass only the brand color. Some theme overrides can lower the contrast ratio below AA, see Palette overrides. Your application code does not change. You only append params to the frame URL.

Quick start

<iframe
  src="https://platform.moonpay.com/otp-auth?brandColor=FF6B00&clientToken=..."
  width="420"
  height="640"
/>
That’s it. The frame renders with your brand applied. For more control, pass a customTheme alongside brandColor (see Custom theme).

Initialization parameters

PropertyTypeRequiredDescription
brandColorstringNoA color value that seeds the entire palette. See accepted formats. If you omit this, the frame renders with the default MoonPay theme.
customThemestringNoA URL-encoded JSON object that overrides specific aspects of the theme. Use this for border radius, color scheme, and palette controls. See Custom theme.

Brand color formats

The simplest form is a six-digit hex with or without the #:
brandColor=FF6B00         # bare hex (simplest)
brandColor=%23FF6B00      # URL-encoded #FF6B00
brandColor=rgb(255,107,0) # CSS function form
brandColor=0xFF6B00       # numeric hex
All forms above produce the same theme. Three-digit shorthand (#F60), rgba(), hsl(), and oklch() are also accepted. If the value cannot be parsed as a color, the param is ignored and the frame falls back to the default theme. No error is surfaced.

Custom theme

Pass a JSON object describing the overrides you want and append it to the URL as customTheme. Build the query string with URLSearchParams or wrap the JSON in encodeURIComponent so the frame receives a valid URL-encoded payload.

Building the URL

const customTheme = JSON.stringify({
  borderRadius: "l",
  colorScheme: "light",
  palette: {
    minContrastRatio: "AAA",
  },
});

const params = new URLSearchParams({
  brandColor: "FF6B00",
  customTheme,
});

const url = `https://platform.moonpay.com/otp-auth?${params.toString()}`;
The frame parses the param, validates each field, applies what it understands, and ignores the rest. If the value doesn’t parse as JSON, the param is dropped and the frame falls back to the brand-color-only behavior.

Theme shape

FieldTypeRequiredDescription
borderRadius"none", "s", "m", "l", "full"NoNamed radius scale applied across the frame. Defaults to "m". See Border radius.
colorScheme"auto", "light", "dark"NoLocks the frame to a specific color scheme. Defaults to "auto", which follows the user’s OS preference.
paletteobjectNoFiner controls for color derivation. See Palette overrides.
Any field you omit keeps its default. Unknown fields are silently ignored, so the shape can grow over time without breaking existing integrations.

Border radius

borderRadius accepts a named scale. Each value maps to the full set of radii the frame uses.
ValueButtons & inputsCardsSheets
none000
s4px8px12px
m (default)12px16px24px
l16px24px32px
full9999px32px40px
full makes interactive elements fully rounded while keeping larger surfaces visually bounded.

Color scheme

colorScheme controls which mode the frame renders in.
ValueBehavior
auto (default)Follows the user’s OS prefers-color-scheme setting.
lightAlways renders the light palette regardless of OS preference.
darkAlways renders the dark palette regardless of OS preference.
Use light or dark if your app only ships one mode and you want the embedded frame to match.

Palette overrides

Optional. Use these only if you need finer control than brandColor alone provides.
FieldTypeRequiredDescription
minContrastRatio"AA", "AAA"NoMinimum contrast ratio enforced between text and its background. Defaults to "AA" (4.5:1).
semanticColors{ positive?, negative?, caution? }NoOverrides the base colors used to derive success, error, and warning palettes. Each accepts the same formats as brandColor.
Example:
const customTheme = {
  palette: {
    minContrastRatio: "AAA",
    semanticColors: {
      positive: "#00C896",
      negative: "#E53E3E",
    },
  },
};

What gets themed

The brand color and theme overrides seed every color and radius the frame uses. The table below shows the role each part of the UI plays.
SurfaceColor family
Page backgroundSurface, with three nested card tiers
Primary CTAsAccent (your brand)
Inputs and bordersSurface tiers, with accent on focus
Success messagingPositive, derived from a green base and adjusted to fit your palette
Error messagingNegative, derived from a red base and adjusted to fit your palette
Warning messagingCaution, derived from an amber base and adjusted to fit your palette
TextForeground colors paired to each surface for AA contrast
Dark modeInverted counterpart of every color above, derived from the same seed

Dark mode

Dark mode is automatic. The frame respects the user’s OS prefers-color-scheme setting unless you set colorScheme to light or dark in your customTheme. Both light and dark themes are derived from the single brand color you pass, so you do not need to supply a separate dark color.

Accessibility

Every derived text and background pair meets WCAG AA contrast (4.5:1 for normal text) by default. Raise to AAA (7:1) by setting palette.minContrastRatio to "AAA" in your customTheme. If your brand color is extreme enough that the target ratio would be impossible for some role, the frame adjusts that role within a perceptually uniform color space to maintain accessibility. Your brand color itself is preserved as the accent.

Examples

OTP authentication, brand color only

<iframe
  src="https://platform.moonpay.com/otp-auth?clientToken=...&publicKey=...&channelId=...&brandColor=FF6B00"
/>

Add card, rounded shape, locked to light

<!-- customTheme = { borderRadius: "l", colorScheme: "light" } -->
<iframe
  src="https://platform.moonpay.com/add-card?channelId=...&country=GB&brandColor=FF6B00&customTheme=%7B%22borderRadius%22%3A%22l%22%2C%22colorScheme%22%3A%22light%22%7D"
/>

Identity passback, AAA contrast

<!-- customTheme = { palette: { minContrastRatio: "AAA" } } -->
<iframe
  src="https://platform.moonpay.com/identity-passback?channelId=...&clientToken=...&brandColor=FF6B00&customTheme=%7B%22palette%22%3A%7B%22minContrastRatio%22%3A%22AAA%22%7D%7D"
/>

Reset, fully rounded, custom status colors

<!--
  customTheme = {
    borderRadius: "full",
    palette: {
      semanticColors: { positive: "#00C896", negative: "#E53E3E" }
    }
  }
-->
<iframe
  src="https://platform.moonpay.com/reset?channelId=...&brandColor=FF6B00&customTheme=%7B%22borderRadius%22%3A%22full%22%2C%22palette%22%3A%7B%22semanticColors%22%3A%7B%22positive%22%3A%22%2300C896%22%2C%22negative%22%3A%22%23E53E3E%22%7D%7D%7D"
/>

Troubleshooting

The frame renders in default MoonPay colors despite passing brandColor. Check that the value parses as a color. The simplest form is a bare six-digit hex, for example brandColor=FF6B00. If you include the #, URL-encode it as %23 (brandColor=%23FF6B00). My customTheme does not apply. Verify the value parses as JSON once URL-decoded. Build the query string with URLSearchParams or wrap the JSON in encodeURIComponent so characters like {, }, ", and : are escaped. If the JSON is malformed or the field values are out of range, the entire customTheme is dropped and the frame falls back to the brand-color-only behavior. Can I pass different colors for light and dark? Not yet. Both light and dark themes are derived from the single brandColor you provide. Per-mode overrides are on the roadmap. How can I confirm theming is applied? Open browser devtools and inspect the frame’s <head>. Look for <style data-partner-theme>.... Its presence means the partner theme is active.

What’s next

Per-mode brand overrides are on the roadmap, with a separate brandColor for light and dark. The contract is additive, so you will be able to adopt it without changing existing integrations. A specific timeline will be published when it lands.