# Using the Platform API Source: https://dev.moonpay.com/api-reference/platform/documentation/using-the-api Get started with the MoonPay Platform API Looking for the widget API? Take a look at the [reference here](/api-reference/widget). ## Capability enablement **Capability access is enabled per partner.** Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. Requests to an endpoint for a capability that is not enabled fail with a plain `404` (`Cannot POST /...`) rather than a permissions error. If you get an unexpected `404` on an endpoint you expect access to, contact your MoonPay account team or [team@moonpay.com](mailto:team@moonpay.com) to have the capability enabled. ## Base URL ```bash theme={null} https://api.moonpay.com ``` Test mode vs live mode is determined by the API key you use, not the URL. Use test API key (`sk_test_...`) for test mode and live API keys (`sk_live_...`) for production. ## Authentication Authentication depends on whether you’re calling an endpoint from your server or from the client. ### Server-side Authentication For server-side requests, send your [secret key](/platform/guides/api-and-sdk-credentials#secret-key) in the `X-Api-Key` header. ```ts fetch theme={null} const URL = "https://api.moonpay.com/platform/v1/sessions"; const res = await fetch(URL, { headers: { "Content-Type": "application/json", "X-Api-Key": "sk_test_123", }, method: "POST", body: JSON.stringify({ externalCustomerId: "your_user_id", deviceIp: "203.0.113.1", }), }); ``` ```sh curl theme={null} curl -X POST "https://api.moonpay.com/platform/v1/sessions" \ -H "Content-Type: application/json" \ -H "X-Api-Key: sk_test_123" \ -d '{ "externalCustomerId": "customer1", "deviceIp": "203.0.113.1" }' ``` ### Client-side Authentication For client-side API requests, use the [`accessToken`](/platform/guides/api-and-sdk-credentials#access-token) returned from a connection as a [Bearer token](https://swagger.io/docs/specification/v3_0/authentication/bearer-authentication/). ```ts fetch theme={null} await fetch("https://api.moonpay.com/platform/v1/quotes", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ // ... request body ... }), }); ``` ## Response Format Responses are returned as `JSON` with the `content-type: application/json` header. ## Pagination Some requests, like [listing transactions](/api-reference/platform/endpoints/transactions/list), return paginated results using cursor-based pagination. Each response includes a cursor string that you pass to the next request to fetch the next page. The response includes a `pageInfo` object. If `pageInfo.nextCursor` is `null`, there are no more pages. For example: ```json theme={null} { "data": [], "pageInfo": { "nextCursor": "tr_123e4567-e89b-12d3-a456-426614174000" } } ``` ## Rate limits Currently, requests for this integration are limited to 30 per second. ## Debugging Each API response includes a request ID header. Use this ID when working with support: ```bash Example theme={null} X-Request-Id: some-value ``` ## Error Handling When a request fails (4xx), the API returns an error object with details: ```json Example error response theme={null} { "code": 400, "type": "Invalid request", "message": "Invalid request. sourceAmount must be greater than 0." } ``` ## OpenAPI The API follows the [OpenAPI 3.1](https://swagger.io/specification/) specification. You can use the spec to generate typed clients for any language. See the [OpenAPI Spec](/platform/guides/openapi-codegen) page for the full specification and code generation instructions. # List assets Source: https://dev.moonpay.com/api-reference/platform/endpoints/assets/list GET /platform/v1/assets List the crypto assets available for purchase, including DeFi tokens Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Export customer data Source: https://dev.moonpay.com/api-reference/platform/endpoints/customers/export POST /platform/v1/customers/export Export MoonPay-verified customer data using a consent token Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Get a customer Source: https://dev.moonpay.com/api-reference/platform/endpoints/customers/get GET /platform/v1/customers/{id} Get a customer's details and KYC standing Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Get a file upload URL Source: https://dev.moonpay.com/api-reference/platform/endpoints/customers/get-upload-url POST /platform/v1/customers/{id}/files/upload-url Get a single-use presigned URL to upload a customer's identity documents Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Confirm uploaded files Source: https://dev.moonpay.com/api-reference/platform/endpoints/customers/submit-files POST /platform/v1/customers/{id}/files Confirm uploaded files and attach them to the customer's KYC session Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Submit KYC data Source: https://dev.moonpay.com/api-reference/platform/endpoints/customers/submit-kyc PATCH /platform/v1/customers/{id}/kyc Submit outstanding KYC requirements for a customer Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Delete a payment method Source: https://dev.moonpay.com/api-reference/platform/endpoints/payment-methods/delete DELETE /platform/v1/payment-methods/{paymentMethodId} Remove a stored payment method for a customer Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # List payment methods Source: https://dev.moonpay.com/api-reference/platform/endpoints/payment-methods/list GET /platform/v1/payment-methods Get available payment method configurations for a user Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Get a quote Source: https://dev.moonpay.com/api-reference/platform/endpoints/quotes/get POST /platform/v1/quotes/buy Build quotes for fiat->crypto transactions Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). ## Identifying the destination token The destination crypto asset is identified differently depending on whether it is a centralised (CeFi) or DeFi token: * **CeFi assets** — pass `destination.code` (e.g. `ETH`, `USDC_SOL`). * **DeFi tokens** — pass `destination.caip19`, the [CAIP-19](https://chainagnostic.org/CAIPs/caip-19) identifier returned by the [list assets](/api-reference/platform/endpoints/assets/list) endpoint. A DeFi token's `code` is not unique, so `caip19` is required to disambiguate it. Provide exactly one. If both are sent, `caip19` takes precedence. # Create a session Source: https://dev.moonpay.com/api-reference/platform/endpoints/sessions/create POST /platform/v1/sessions Create a session token to initialize a connection Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Revoke a session Source: https://dev.moonpay.com/api-reference/platform/endpoints/sessions/revoke DELETE /platform/v1/sessions Revoke an active session token Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Get a transaction Source: https://dev.moonpay.com/api-reference/platform/endpoints/transactions/get GET /platform/v1/transactions/{id} Get details for a single transaction by ID Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # List transactions Source: https://dev.moonpay.com/api-reference/platform/endpoints/transactions/list GET /platform/v1/transactions List transactions for the connected user with optional date filtering and pagination Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). # Simulate bank-transfer settlement Source: https://dev.moonpay.com/api-reference/platform/endpoints/transactions/simulate-bank-transfer POST /platform/v1/transactions/{id}/simulate-bank-transfer Sandbox only. Drive a waiting-payment bank-transfer transaction to a terminal outcome without real funds. Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. If this endpoint returns an unexpected `404`, see [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement). This endpoint works only in [test mode](/platform/overview/test-mode#bank-transfers). Live-mode transactions return `403`. In production, bank transfers settle when the customer's deposit arrives. # Asset Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/asset A fiat currency or crypto token This is the compact asset shape embedded in a [quote](/api-reference/platform/objects-and-types/quote). For the full catalogue of purchasable tokens — including DeFi tokens and their on-chain metadata — see the [list assets](/api-reference/platform/endpoints/assets/list) endpoint. ## Properties | Property | Type | Required | Description | | ----------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | | `code` | string | Yes | The currency code or token symbol (e.g., `USD`, `ETH`, `BTC`) | | `name` | string | No | The human-readable name (e.g., `US Dollar`, `Ethereum`) | | `precision` | integer | No | The number of supported decimal places | | `caip19` | string | No | The [CAIP-19](https://chainagnostic.org/CAIPs/caip-19) asset identifier. Present for DeFi tokens, whose `code` is not unique. | ## Example ```json theme={null} { "code": "ETH", "name": "Ethereum", "precision": 18 } ``` A DeFi token additionally carries its `caip19`: ```json theme={null} { "code": "PENGU", "name": "Pudgy Penguins", "precision": 0, "caip19": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:2zMMhcVQEXDtdE6vsFS7S7D5oUodfJHE8vd1gnBouauv" } ``` ## Common Assets ### Fiat Currencies | Code | Name | Precision | | ----- | ------------- | --------- | | `USD` | US Dollar | 2 | | `EUR` | Euro | 2 | | `GBP` | British Pound | 2 | ### Cryptocurrencies | Code | Name | Precision | | ------ | -------- | --------- | | `BTC` | Bitcoin | 8 | | `ETH` | Ethereum | 18 | | `USDC` | USD Coin | 6 | # Customer Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/customer A customer and their KYC standing Returned by the [Customer API](/platform/guides/customer-api) endpoints. The `kyc` object tells you whether the customer can transact, what's outstanding, and how to submit it. ## Properties | Property | Type | Required | Description | | -------------------- | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- | | `id` | string (uuid) | Yes | The customer's unique MoonPay identifier | | `externalCustomerId` | string | Yes | Your identifier for this customer, if one was supplied. `null` when the customer connected without an external identifier | | `kyc` | object | Yes | KYC details for the customer | ## KYC | Property | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------- | | `status` | string | Yes | The customer's KYC standing with you. See [KYC status](#kyc-status) | | `requirements` | object | Yes | Outstanding and completed KYC requirements, keyed by category. See [Requirements](#requirements) | | `challenge` | object | No | Hosted challenge you must surface to the customer to complete verification. See [Challenge](#challenge) | ## KYC status | Value | Description | | ------------- | ----------------------------------------------------------------------------------------------- | | `not_created` | No KYC session exists yet for this customer with your account. | | `collecting` | The customer has outstanding requirements to submit. | | `verifying` | MoonPay is processing the submitted data. No action is needed from you or the customer. | | `active` | KYC is complete. The customer is in good standing. | | `unavailable` | KYC cannot proceed for this customer (for example, an unsupported region, or a closed account). | ## Requirements Each key is a requirement category. Only categories that apply to the customer are present. Each entry has the same shape: | Property | Type | Required | Description | | ---------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `status` | string | Yes | `incomplete` or `complete` | | `requiredFields` | array | No | When `status` is `incomplete`, the outstanding fields the customer must provide. Omitted when the category does not surface field-level detail | | Category | Description | | -------------------- | ----------------------------------------------------------------------------------------- | | `basicDetails` | The customer's basic personal details (first name, last name, date of birth, nationality) | | `residentialAddress` | The customer's residential address | | `identityDocuments` | Identity document submission (passport, driver's license, residence permit) | | `selfie` | Selfie photo submission | | `taxIdentifiers` | Tax identifier submission (SSN, CPF, TIN) | | `proofOfAddress` | Proof-of-address document submission | | `phoneNumber` | The customer's phone number in [E.164](https://en.wikipedia.org/wiki/E.164) format | | `questionnaires` | Due-diligence questionnaires the customer must complete | For the fields and documents each country requires, see [KYC data requirements](/platform/guides/kyc-data-requirements). ## Challenge | Property | Type | Required | Description | | ----------- | ------------------ | -------- | -------------------------------------------------------------------------------------- | | `url` | string | Yes | Fully-formed challenge URL. Render it directly; the URL embeds the challenge token | | `expiresAt` | string (date-time) | Yes | When the challenge URL expires. Surface the challenge to the customer before this time | ## Example ```json theme={null} { "id": "c1a2b3c4-0000-4000-8000-000000000000", "externalCustomerId": "your_user_id", "kyc": { "status": "collecting", "requirements": { "basicDetails": { "status": "complete" }, "residentialAddress": { "status": "incomplete", "requiredFields": ["street", "locality", "postalCode"] }, "identityDocuments": { "status": "incomplete" } } } } ``` # Customer export Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/customer-export MoonPay-verified customer data returned by the export endpoint Returned by [Export customer data](/api-reference/platform/endpoints/customers/export). All fields are populated from the customer's latest approved verification. Fields MoonPay does not hold are `null`. ## Properties | Property | Type | Required | Description | | -------------------- | ------ | -------- | ----------------------------------------------------------------------------- | | `basicDetails` | object | Yes | The customer's basic personal details | | `residentialAddress` | object | Yes | The customer's residential address | | `phoneNumber` | object | Yes | The customer's phone number | | `taxIdentifiers` | array | Yes | Tax identifiers recorded for the customer. Empty when none are recorded | | `files` | array | Yes | Identity documents the customer submitted, each with a presigned download URL | ## Basic details | Property | Type | Required | Description | | ------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------- | | `firstName` | string | Yes | The customer's first name. `null` for mononymous customers recorded with only a last name | | `lastName` | string | Yes | The customer's last name. `null` for mononymous customers recorded with only a first name | | `dateOfBirth` | string | Yes | The customer's date of birth in `YYYY-MM-DD` format | | `nationality` | string | Yes | The customer's nationality as an [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) country code | ## Residential address | Property | Type | Required | Description | | -------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `country` | string | Yes | Country as an [ISO 3166-1 alpha-3](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3) country code | | `administrativeArea` | string | No | State, province, or territory. Present when the country has subdivisions recognized by MoonPay (currently USA and CAN); omitted otherwise | | `locality` | string | Yes | City, town, or locality | | `street` | string | Yes | First line of the address (street) | | `subStreet` | string | No | Second line of the address (apartment, unit, building). Omitted when MoonPay does not hold this value | | `postalCode` | string | Yes | Postal or ZIP code | ## Phone number | Property | Type | Required | Description | | -------- | ------ | -------- | ---------------------------------------------------------------------------------- | | `number` | string | Yes | The customer's phone number in [E.164](https://en.wikipedia.org/wiki/E.164) format | ## Tax identifiers | Property | Type | Required | Description | | --------- | ------ | -------- | ----------------------------------------------------------------------------------------------------- | | `type` | string | Yes | Type of tax identifier: `tin`, `ssn`, or `cpf` | | `country` | string | No | Issuing country as an ISO 3166-1 alpha-3 code. Present for `tin` entries; omitted for `ssn` and `cpf` | | `value` | string | Yes | Identifier value as recorded by MoonPay | ## Files | Property | Type | Required | Description | | ------------- | ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------- | | `id` | string (uuid) | Yes | MoonPay's identifier for the file | | `type` | string | Yes | Type of file. See [File types](#file-types) | | `side` | string | No | `front` or `back`. Present for two-sided document types; omitted for single-sided | | `uploadedAt` | string (date-time) | Yes | When MoonPay received the file | | `downloadUrl` | string (uri) | Yes | Presigned download URL. Expires 60 minutes after the response is returned; do not attach your API key when fetching it | ### File types | Value | Sides | | ------------------------ | ------------ | | `passport` | Single-sided | | `driving_licence` | Two-sided | | `national_identity_card` | Two-sided | | `residence_permit` | Two-sided | | `selfie` | Single-sided | | `proof_of_address` | Single-sided | ## Example ```json theme={null} { "basicDetails": { "firstName": "Jane", "lastName": "Doe", "dateOfBirth": "1990-01-15", "nationality": "USA" }, "residentialAddress": { "country": "USA", "administrativeArea": "NY", "locality": "New York", "street": "350 Fifth Avenue", "subStreet": "Apt 1A", "postalCode": "10118" }, "phoneNumber": { "number": "+14155551234" }, "taxIdentifiers": [{ "type": "ssn", "value": "078-05-1120" }], "files": [ { "id": "f1a2b3c4-0000-4000-8000-000000000000", "type": "passport", "uploadedAt": "2026-06-30T14:30:50.000Z", "downloadUrl": "https://files.moonpay.com/exports/f1a2b3c4...?signature=..." } ] } ``` # Fees Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/fees Fee breakdown for an operation ## Properties | Property | Type | Required | Description | | ----------- | ------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `network` | [MonetaryAmount](/api-reference/platform/objects-and-types/fees#monetaryamount) | No | The network fee (e.g., gas fee for blockchain transactions) | | `moonpay` | [MonetaryAmount](/api-reference/platform/objects-and-types/fees#monetaryamount) | No | The MoonPay processing fee | | `ecosystem` | [MonetaryAmount](/api-reference/platform/objects-and-types/fees#monetaryamount) | No | The ecosystem fee, if applicable | | `defi` | [MonetaryAmount](/api-reference/platform/objects-and-types/fees#monetaryamount) | No | The DeFi swap fee. Present only for Gateway (DeFi) swaps that carry an itemized swap fee; on-chain fees are reflected in `destination.amount` instead | | `partner` | [MonetaryAmount](/api-reference/platform/objects-and-types/fees#monetaryamount) | No | **Deprecated.** Use `ecosystem` instead. This field will be removed in a future release. The partner's fee, if applicable | ## MonetaryAmount Each fee is represented as a monetary amount: | Property | Type | Required | Description | | -------------- | ------ | -------- | ---------------------------------------------------- | | `amount` | string | Yes | The numeric amount as a string to preserve precision | | `currencyCode` | string | Yes | The currency code (e.g., `USD`) | ## Example ```json theme={null} { "network": { "amount": "2.50", "currencyCode": "USD" }, "moonpay": { "amount": "3.99", "currencyCode": "USD" }, "ecosystem": { "amount": "1.00", "currencyCode": "USD" }, "partner": { "amount": "1.00", "currencyCode": "USD" } } ``` All fee amounts are strings to preserve decimal precision. Parse them appropriately in your application. # Payment Method Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/payment-method A payment method configuration with its capabilities and availability ## Properties | Property | Type | Required | Description | | -------------- | ------ | -------- | -------------------------------------------------- | | `type` | string | Yes | Payment method type (e.g., `apple_pay`) | | `capabilities` | object | Yes | What this payment method supports | | `availability` | object | Yes | Whether this payment method is currently available | ## Capabilities | Property | Type | Required | Description | | --------------------------- | --------- | -------- | ---------------------------------------------------------- | | `supportedCurrencies` | string\[] | Yes | Currencies supported (e.g., `["USD", "EUR"]`) | | `supportedTransactionTypes` | string\[] | Yes | Transaction types supported (e.g., `["buy", "sell"]`) | | `allowsDeletion` | boolean | Yes | Whether this payment method can be deleted | | `requiresWidget` | boolean | Yes | Whether completing the payment requires the MoonPay widget | ## Availability | Property | Type | Required | Description | | --------- | --------- | -------- | -------------------------------------------------- | | `active` | boolean | Yes | Whether the payment method is currently available | | `reasons` | string\[] | No | If unavailable, a list of machine-readable reasons | ## Payment Method Types | Type | Description | | ------------ | --------------------------- | | `apple_pay` | Apple Pay | | `google_pay` | Google Pay | | `card` | Stored credit or debit card | | `sepa` | SEPA bank transfer | The fiat currencies each method supports come from `capabilities.supportedCurrencies` (see [Capabilities](#capabilities)); `sepa` bank transfers are EUR only. Bank transfers (`sepa`) are a floating payment method: the quote is an estimate, and the final amount is set when the customer's funds settle. See [Exchange rate type](/api-reference/platform/objects-and-types/quote#exchange-rate-type). ## Example ```json theme={null} { "type": "apple_pay", "capabilities": { "supportedCurrencies": ["USD", "EUR", "GBP"], "supportedTransactionTypes": ["buy"], "allowsDeletion": false, "requiresWidget": false }, "availability": { "active": true } } ``` ## Checking Availability Always check `availability.active` before offering a payment method to users. If `active` is `false`, check `reasons` for machine-readable details: ```json theme={null} { "type": "apple_pay", "capabilities": { "supportedCurrencies": ["USD"], "supportedTransactionTypes": ["buy"], "allowsDeletion": false, "requiresWidget": false }, "availability": { "active": false, "reasons": ["maintenance"] } } ``` # Quote Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/quote A quote for a buy transaction with an exchange rate, fees, and expiry ## Properties | Property | Type | Required | Description | | -------------------- | ------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------- | | `source` | object | Yes | Source amount and asset (fiat currency) you send | | `destination` | object | Yes | Destination amount and asset (cryptocurrency) the customer receives | | `fees` | [Fees](/api-reference/platform/objects-and-types/fees) | Yes | Breakdown of network, MoonPay, and ecosystem fees | | `wallet` | [Wallet](/api-reference/platform/objects-and-types/wallet) \| null | Yes | Wallet address where crypto will be sent. Null if not yet provided | | `paymentMethod` | object \| null | Yes | Payment method used for this quote. Null if not specified | | `expiresAt` | string (date-time) | Yes | ISO 8601 datetime when the quote expires | | `executable` | boolean | Yes | Whether the quote can be executed | | `feeBehavior` | string | Yes | Effective fee behavior, `inclusive` or `exclusive`. See [Fee behavior](#fee-behavior). | | `exchangeRate` | string | Yes | The rate used to convert between fiat and crypto, as the fiat value of one unit of crypto. Pairs with `exchangeRateType`. | | `exchangeRateType` | string | Yes | Whether the exchange rate is `fixed` or `floating`. See [Exchange rate type](#exchange-rate-type). | | `signature` | string | Yes | Signature for mounting a payment frame | | `paymentDisclosures` | array | No | Disclosure(s) to render for this transaction. Empty array means none required. | | `challenge` | [Challenge](#challenge) | No | A step the customer must complete before this quote can be executed. See [Challenge](#challenge). | ## Source / Destination Both `source` and `destination` have the same structure: | Property | Type | Required | Description | | -------- | -------------------------------------------------------- | -------- | -------------------------------------------- | | `amount` | string | Yes | The amount as a string to preserve precision | | `asset` | [Asset](/api-reference/platform/objects-and-types/asset) | Yes | The currency or token | ## Fee behavior `feeBehavior` describes how fees relate to `source.amount` when you request a quote by `source.amount`: * `inclusive` (default): the customer pays exactly `source.amount`, and fees are carved out of it. * `exclusive`: fees are added on top of `source.amount`. The full `source.amount` is converted, so the customer receives more crypto than the inclusive quote for the same input. Set `feeBehavior` on the buy-quote request to choose the behavior. It applies only to source-amount quotes. When you quote by `destination.amount`, MoonPay ignores the request value and the quote is always fees-inclusive. The response always echoes the effective `feeBehavior`. ## Exchange rate type `exchangeRateType` tells you whether the quoted amount is final or an estimate, and pairs with the `exchangeRate` field: * `fixed`: the exchange rate is locked at quote time. Card and wallet methods (card, Apple Pay, Google Pay) return `fixed`, and the quoted amount is final. * `floating`: the exchange rate is an estimate confirmed when the payment settles. Bank transfers (SEPA) return `floating`. When `exchangeRateType` is `floating`, render the estimated crypto amount with a tilde (for example, `~0.2345 BTC`) and tell the customer the final amount is set when the transfer settles. Bank transfers don't support DeFi assets. If you request a bank-transfer quote (`sepa`) for a destination identified by `caip19`, the request fails with `400` and the message "Bank transfers are not supported for DeFi assets." Offer a card or wallet payment method for these assets instead. ## Challenge `challenge` is present when the customer must complete a step before this quote can be executed. It arrives alongside `executable: false`. Mount the [challenge frame](/platform/frames/challenge) at `url`, then request the quote again once the challenge resolves. | Property | Type | Required | Description | | -------- | ------ | -------- | -------------------------------------------------------------------------------------------- | | `kind` | string | Yes | How to resolve the challenge. Always `frame` today. | | `url` | string | Yes | Fully-formed challenge frame URL. Render it directly. Do not construct, parse, or modify it. | ```json theme={null} { "executable": false, "challenge": { "kind": "frame", "url": "https://platform.moonpay.com/v2/challenge?challengeToken=eyJhbGciOiJFUzI1NiIs..." } } ``` Guest checkout limit upgrades are the one flow that returns `challenge` on a quote today. See [Upgrade a guest account](/platform/guides/guest-checkout#raise-the-limit-with-a-step-up). ## Using the Quote Pass the `signature` to the payment frame when you mount it. The frame uses this signature along with the quote data to execute the payment and create the transaction. Quotes expire. Check `expiresAt` and create the transaction before this time. If `executable` is `false`, the customer must provide additional information before you can use this quote. When the quote also carries a [`challenge`](#challenge), that object tells you what to render to collect it. ## Payment disclosures Each item in `paymentDisclosures` identifies a specific piece of text you must render verbatim. | Property | Type | Description | | --------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | string | Identifies a specific disclosure. See the ID table below. | | `version` | string | Increments on any wording change. If you receive an unrecognised `version`, fall back to the most-conservative disclosure and log an alert so your integration team can update the copy. | ### Disclosure IDs | ID | Description | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `us-transaction-finality` | Required in NY and WA (NYDFS / state money-transmitter rules). See [disclosure copy](/platform/overview/going-live#disclosures---us-new-york-and-washington). | | `eea-crypto-asset-risk` | EEA standard crypto-asset risk disclosure (MiCA) | | `eea-unregulated-stablecoin-risk` | EEA disclosure for non-MiCA-compliant stablecoins (USDT, DAI, PYUSD) | | `gateway-token` | Required when buying a DeFi token via Gateway. See [disclosure copy](/platform/overview/going-live#disclosures---defi-tokens-gateway). | ## Example ```json theme={null} { "source": { "amount": "100.00", "asset": { "code": "USD", "name": "US Dollar", "precision": 2 } }, "destination": { "amount": "0.0025", "asset": { "code": "ETH", "name": "Ethereum", "precision": 18 } }, "fees": { "network": { "amount": "2.50", "currencyCode": "USD" }, "moonpay": { "amount": "3.99", "currencyCode": "USD" } }, "wallet": { "address": "0x1234...abcd" }, "paymentMethod": { "type": "apple_pay" }, "expiresAt": "2026-01-29T14:35:50.000Z", "executable": true, "feeBehavior": "inclusive", "exchangeRate": "87523.17", "exchangeRateType": "fixed", "signature": "eyJhbGciOiJIUzI1NiIs...", "paymentDisclosures": [ { "id": "eea-crypto-asset-risk", "version": "1" } ] } ``` # Transaction Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/transaction A transaction representing a crypto purchase or sale ## Properties | Property | Type | Required | Description | | ------------------------- | ---------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | string | Yes | The MoonPay ID of the transaction | | `createdAt` | string (date-time) | Yes | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of when the transaction was created | | `updatedAt` | string (date-time) | Yes | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) timestamp of when the transaction was last updated | | `status` | string | Yes | The current status: `completed`, `failed`, or `pending` | | `source` | object | Yes | The source amount and asset (fiat currency) | | `destination` | object | Yes | The destination amount and asset (cryptocurrency) | | `fees` | [Fees](/api-reference/platform/objects-and-types/fees) | Yes | Fee breakdown for the transaction | | `wallet` | [Wallet](/api-reference/platform/objects-and-types/wallet) | Yes | The wallet where crypto was delivered | | `customer` | object | Yes | The customer who made the transaction | | `paymentMethod` | object | No | The payment method used | | `stages` | array | No | The stages of the transaction lifecycle | | `bankTransferDepositInfo` | object | No | Bank account details and payment reference for completing a bank transfer. Present only on bank-transfer transactions (SEPA). See [Bank transfer deposit info](#bank-transfer-deposit-info). | ## Source / Destination Both `source` and `destination` have the same structure: | Property | Type | Required | Description | | -------- | -------------------------------------------------------- | -------- | -------------------------------------------- | | `amount` | string | Yes | The amount as a string to preserve precision | | `asset` | [Asset](/api-reference/platform/objects-and-types/asset) | Yes | The currency or token | ## Customer | Property | Type | Required | Description | | -------- | ------ | -------- | ------------------------------ | | `id` | string | Yes | The MoonPay ID of the customer | ## Transaction Status | Value | Description | | ----------- | ---------------------------------- | | `pending` | Transaction is in progress | | `completed` | Transaction completed successfully | | `failed` | Transaction failed | ## Bank transfer deposit info Bank-transfer payments (SEPA) settle when the customer sends funds to a MoonPay bank account. For these transactions, the response includes a `bankTransferDepositInfo` object with the account details and the payment reference. Render these details in your own UI so the customer can complete the transfer, then poll the transaction until it reaches a terminal status. Bank-transfer transactions stay `pending` until the customer's funds arrive. | Property | Type | Required | Description | | ------------------ | ------ | -------- | ------------------------------------------------------------------ | | `reference` | string | Yes | The payment reference the customer must include with the transfer. | | `recipientName` | string | Yes | The name of the recipient that receives the funds. | | `recipientAddress` | string | Yes | The address of the recipient. | | `iban` | string | No | The IBAN. Provided for SEPA (EUR). | | `bic` | string | No | The BIC (SWIFT code). Provided for SEPA (EUR). | | `bankName` | string | No | The name of the receiving bank. | | `bankAddress` | string | No | The address of the receiving bank. | The customer must always include the payment `reference`. Transfers sent without the reference are rejected. ## Example ```json theme={null} { "id": "tr_abc123", "createdAt": "2026-01-29T14:30:50.000Z", "updatedAt": "2026-01-29T15:30:50.000Z", "status": "completed", "source": { "amount": "100.00", "asset": { "code": "USD" } }, "destination": { "amount": "0.0025", "asset": { "code": "ETH" } }, "fees": { "network": { "amount": "2.50", "currencyCode": "USD" }, "moonpay": { "amount": "3.99", "currencyCode": "USD" } }, "wallet": { "address": "0x1234...abcd" }, "customer": { "id": "cust_xyz789" }, "paymentMethod": { "type": "apple_pay" } } ``` # Wallet Source: https://dev.moonpay.com/api-reference/platform/objects-and-types/wallet A blockchain wallet address ## Properties | Property | Type | Required | Description | | --------- | ------ | -------- | ---------------------------------------------------------------------------- | | `address` | string | Yes | The wallet address | | `tag` | string | No | An optional memo or destination tag (used by some blockchains like XRP, XLM) | ## Example ### Standard Wallet ```json theme={null} { "address": "0x1234567890abcdef1234567890abcdef12345678" } ``` ### Wallet with Memo/Tag Some blockchains like XRP and XLM require a destination tag or memo to route funds to the correct account: ```json theme={null} { "address": "rN7n3473SaZBCG4dFL83w7a1RXtXtbk2D9", "tag": "12345678" } ``` For blockchains that require tags/memos (XRP, XLM, etc.), always include the `tag` field when provided. Missing tags can result in lost funds. # Cancel Sell transaction Source: https://dev.moonpay.com/api-reference/widget/cancelselltransaction DELETE /v3/sell_transactions/{transactionId} Cancels a sell transaction. This endpoint will return HTTP status 204 No Content if the sell transaction was successfully canceled. If sell transaction could not be canceled (e.g. because it has already been completed) it will return HTTP status 409 Conflict. # Errors Source: https://dev.moonpay.com/api-reference/widget/errors The error response shape, the moonPayErrorCode catalog, and how to resolve common errors Every error from the widget API is a JSON body with a stable, machine-readable `moonPayErrorCode`. This page documents the response shape, the codes you can encounter, and what to do about them. ## Error response shape ```json theme={null} { "moonPayErrorCode": "4_SYS_BAD_REQUEST", "message": "Invalid JSON in request body", "type": "BadRequestError", "errors": [] } ``` | Field | Type | Description | | ------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `moonPayErrorCode` | string | Stable error code, safe to branch on. Codes never change meaning once published. | | `message` | string | Human-readable description. May change over time; do not branch on this text. | | `type` | string | The error class name, e.g. `BadRequestError`, `UnauthorizedError`, `MoonPayApiError`. | | `errors` | array | Field-level validation details when the request body or parameters were invalid: `{ property, message, children }`. Empty or absent otherwise. | ## Code format Codes follow the pattern `{category}_{space}_{name}`: | Prefix | Category | | ------------ | --------------------------------------------------------------------------------------------------- | | `1_SYS_` | General system errors | | `4_SYS_` | Request validation errors | | `5_{SPACE}_` | Domain-specific errors, e.g. `5_TM_` (transactions), `5_PS_` (payments), `5_AUTH_` (authentication) | ## System codes These can occur on any endpoint: | Code | Status | Meaning | What to do | | ---------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `4_SYS_NOT_AUTHORIZED` | 401 | Missing or invalid API key or token | Check you're sending the right credential for the endpoint; see [Authentication](/api-reference/widget/using-the-api#authentication) | | `4_SYS_FORBIDDEN` | 403 | Authenticated but not permitted, often a feature that isn't enabled for your account (e.g. "Bank account verification is not enabled") | If the message names a feature, contact your account manager or support to enable it | | `4_SYS_ACCESS_REVOKED` | 403 | Access has been revoked | Contact support | | `4_SYS_BAD_REQUEST` | 400 | Malformed request: invalid JSON body or failed validation | Fix the request; check `message` and `errors` | | `4_SYS_NOT_FOUND` | 404 | Resource not found | Check the identifier; for customers, they must have at least one session initiated with your key | | `4_SYS_NOT_ALLOWED` | 405 | HTTP method not allowed | Use the method documented on the endpoint page | | `4_SYS_CONFLICT` | 409 | Request conflicts with current resource state | Re-fetch the resource and check its state | | `4_SYS_UNPROCESSABLE_ENTITY` | 422 | Input is well-formed but semantically invalid | Fix the request; check `message` | | `4_SYS_TOO_MANY_REQUESTS` | 429 | Rate limited | Retry with exponential backoff | | `4_SYS_NOT_IMPLEMENTED` | 501 | Endpoint path not implemented | Check the URL; contact support if it matches the docs | | `1_SYS_UNKNOWN` | varies | See below | See below | ## `1_SYS_UNKNOWN` is not always a server error `1_SYS_UNKNOWN` means the error didn't carry a specific code. **The HTTP status tells you whose problem it is:** * **With a 4xx status**, it is a validation error from an older code path. The `message` is accurate and actionable; treat it exactly like a `4_SYS_*` error. For example, URL signing failures return `400` with `1_SYS_UNKNOWN` and messages like `Invalid signature`, `Missing signature`, or `Invalid API key`. That means your [URL signing](/widget/on-ramp/customization/url-signing) implementation needs fixing, not that MoonPay had an internal error. * **With a 500 status**, it is a genuine unexpected error on MoonPay's side. The message is `Internal Server Error`. Retry with backoff; if it persists, contact support with the request details and timestamp. ## Common domain codes Domain codes (`5_*`) are stable and their messages are descriptive. The ones you'll most commonly encounter: | Code | Status | Meaning | What to do | | ----------------------------------- | ------ | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `5_DP_LIVE_MODE_DISABLED` | 403 | Live API key used against the sandbox environment | Use your `pk_test_`/`sk_test_` keys on sandbox | | `5_TM_MIN_BUY_AMOUNT_NOT_MET` | 400 | Amount below the currency's minimum | Check limits via [Get currency limits](/api-reference/widget/getcurrencylimits) | | `5_TM_MAX_BUY_AMOUNT_EXCEEDED` | 400 | Amount above the currency's maximum | Check limits via [Get currency limits](/api-reference/widget/getcurrencylimits) | | `5_TM_INVALID_WALLET_ADDRESS` | 400 | Wallet address invalid for the currency | Validate against the currency's `addressRegex` from [Get currencies](/api-reference/widget/getcurrencies) | | `5_TM_CURRENCY_SUSPENDED` | 422 | Transactions for this currency are temporarily suspended | Retry later; check [Get currencies](/api-reference/widget/getcurrencies) for `isSuspended` | | `5_TM_DEFI_TOKEN_NOT_SUPPORTED` | 400 | DeFi token not supported | Check [Get DeFi tokens](/api-reference/widget/getdefitokens) | | `5_QC_BUY_QUOTE_INVALID_SIGNATURE` | 400 | Quote signature invalid: the quote was tampered with or expired | Fetch a fresh quote and pass its signature through unchanged | | `5_PARTNERS_IP_MISMATCH` | 400 | Customer IP doesn't match `allowedIpAddress` on the signed widget URL | See [IP address matching](/widget/on-ramp/customization/ip-matching) | | `5_PARTNERS_IP_MISSING` | 400 | `allowedIpAddress` missing from a signed widget URL that requires it | See [IP address matching](/widget/on-ramp/customization/ip-matching) | | `5_PS_PAYMENT_METHOD_NOT_AVAILABLE` | 400 | Payment method not available for this customer/currency/region | Offer an alternative payment method | | `5_PS_CURRENCY_NOT_SUPPORTED` | 400 | Currency not supported for this operation | Check [Get currencies](/api-reference/widget/getcurrencies) | | `5_IS_LIMIT_EXCEEDED` | 400 | Transaction amount exceeds the customer's daily limit | Surface the limit to the customer; limits depend on KYC level | | `5_IS_SCA_REQUIRED` | 400 | Strong customer authentication required | Direct the customer through the widget flow | If you hit a `5_*` code that isn't listed here, the `message` describes the cause. The code is stable, so it's safe to handle specific cases in your integration. ## Worked examples **`403` + `4_SYS_FORBIDDEN` + "Bank account verification is not enabled"**: your account doesn't have the named feature enabled. No request change will fix it; ask your account manager or support to enable bank account verification. **`400` + `1_SYS_UNKNOWN` + "Invalid signature"**: your widget URL signature doesn't match what MoonPay computes. Verify you're signing the exact query string (after URL encoding) with your **secret** key, HMAC-SHA256, base64-encoded; see [URL signing](/widget/on-ramp/customization/url-signing). # Get Real-time Buy quote Source: https://dev.moonpay.com/api-reference/widget/getbuyquote GET /v3/currencies/{currencyCode}/buy_quote Get detailed real-time quote based on the provided currency code, base amount, your extra fee percentage, payment method, and the inclusion of the fees. # Get Buy transaction Source: https://dev.moonpay.com/api-reference/widget/getbuytransaction GET /v1/transactions/{transactionId} Retrieve a transaction by id. This call will return an error if no transaction with the supplied identifier exists. # Get Buy transaction by External identifier Source: https://dev.moonpay.com/api-reference/widget/getbuytransactionbyexternalid GET /v1/transactions/ext/{externalTransactionId} Retrieve a transaction by its externalTransactionId. This is the identifier you assigned the transaction when creating it. This endpoint returns an array of objects because we cannot ensure the uniqueness of externalTransactionId. # List Buy transactions Source: https://dev.moonpay.com/api-reference/widget/getbuytransactions GET /v1/transactions Returns an array of Buy transactions that match the criteria in the query parameters. Filtering by customerId, externalCustomerId, or externalTransactionId returns transactions of any status. Without one of those filters, the endpoint returns only pending and completed transactions. Each entry in the array is a separate transaction object. Transactions are listed from newest to oldest. # List supported countries Source: https://dev.moonpay.com/api-reference/widget/getcountries GET /v3/countries Returns the list of countries currently supported by MoonPay. Authentication is optional: pass your publishable `apiKey` to have availability tailored to your account. # List supported currencies Source: https://dev.moonpay.com/api-reference/widget/getcurrencies GET /v3/currencies Returns the list of currencies supported by MoonPay. # Get Crypto Currency limits Source: https://dev.moonpay.com/api-reference/widget/getcurrencylimits GET /v3/currencies/{currencyCode}/limits Returns an object containing minimum and maximum buy amounts including or excluding fees for base and quote currencies. It takes into account the payment method if it's provided, **otherwise it defaults to the payment method with the lowest fees.** # Get customer Source: https://dev.moonpay.com/api-reference/widget/getcustomer GET /v1/customers/{customerId} Returns very basic information about a customer based on their MoonPay ID. For you to be able to retrieve a customer, they must have at least one session initiated with your `Api-Key`. # Get customer by externalId Source: https://dev.moonpay.com/api-reference/widget/getcustomerbyexternalid GET /v1/customers/ext/{customerId} Returns very basic information about a customer based on their external customer ID. For you to be able to retrieve a customer, they must have at least one session initiated with your `API-Key`. Please note that this endpoint returns an array of objects because we cannot ensure the uniqueness of the external customer ID. # Get DeFi token Source: https://dev.moonpay.com/api-reference/widget/getdefitoken GET /v1/defi/token Retrieve defi token for a specific contractAddress and network code # List DeFi tokens Source: https://dev.moonpay.com/api-reference/widget/getdefitokens GET /v1/defi/tokens Search and retrieve a paginated list of defi tokens # Check Customer's IP address Source: https://dev.moonpay.com/api-reference/widget/getipaddress GET /v3/ip_address Returns information about an IP address. If the `isAllowed` flag is set to false, it means that MoonPay accepts citizens of this country but not residents. # Get Crypto network fees Source: https://dev.moonpay.com/api-reference/widget/getnetworkfees GET /v3/currencies/network_fees Returns a set of key-value pairs representing the current network fees of cryptocurrencies against fiat currencies. Supply the codes of the crypto and fiat currencies you are interested in, and MoonPay will return the relevant network fees. # Get off ramp transaction Source: https://dev.moonpay.com/api-reference/widget/getofframptransaction GET /v1/virtual-accounts/transactions/offramp/{transactionId} # Get off ramp transactions Source: https://dev.moonpay.com/api-reference/widget/getofframptransactions GET /v1/virtual-accounts/transactions/offramp # Get on ramp transaction Source: https://dev.moonpay.com/api-reference/widget/getonramptransaction GET /v1/virtual-accounts/transactions/onramp/{transactionId} # Get on ramp transactions Source: https://dev.moonpay.com/api-reference/widget/getonramptransactions GET /v1/virtual-accounts/transactions/onramp # Get Sell quote Source: https://dev.moonpay.com/api-reference/widget/getsellquote GET /v3/currencies/{currencyCode}/sell_quote Returns a set of key-value pairs representing a real-time sell quote for a currency. Supply the currency code, the base amount, your extra fee percentage, the payment method and whether the base amount is inclusive of fees, and MoonPay will return a detailed sell quote. # Get Sell transaction Source: https://dev.moonpay.com/api-reference/widget/getselltransaction GET /v3/sell_transactions/{transactionId} # Get Sell transaction by External identifier Source: https://dev.moonpay.com/api-reference/widget/getselltransactionbyexternalid GET /v3/sell_transactions/ext/{externalTransactionId} Retrieve a transaction by its externalTransactionId. This is the identifier you assigned the transaction when creating it. This endpoint returns an array of objects because we cannot ensure the uniqueness of externalTransactionId. # List Sell transactions Source: https://dev.moonpay.com/api-reference/widget/getselltransactions GET /v3/sell_transactions Returns an array of successful Sell transactions which fulfill criteria supplied in the query parameters. Each entry in the array is a separate transaction object. Transactions will be listed from newest to oldest. This call will return an error if `customerId` is not supplied in the query parameters. # List currencies (v4) Source: https://dev.moonpay.com/api-reference/widget/getv4currencies GET /v4/currencies Returns a paginated list of CeFi and DeFi currencies enriched with MoonPay capability flags (buy, sell, swap). Results are filtered by the caller's IP geolocation and, when an `apiKey` is provided, by the partner account configuration. Pass a customer `authorization` bearer token to apply customer-specific eligibility rules. Use `cursor` and `limit` for cursor-based pagination. When `nextCursor` is `null`, you have reached the last page. # Get virtual accounts Source: https://dev.moonpay.com/api-reference/widget/getvirtualaccounts GET /v1/virtual-accounts # List available payment methods Source: https://dev.moonpay.com/api-reference/widget/listpaymentmethods GET /payments/v1/payment-method-config # Overview Source: https://dev.moonpay.com/api-reference/widget/overview API reference for the MoonPay widget integration. The MoonPay widget integration is powered by two APIs: * **Ramps**: buy and sell quotes; transaction lookups; supported countries, currencies, and payment methods. * **Virtual Accounts**: programmatic on-ramp and off-ramp via virtual bank accounts, plus the associated transaction history. Buy quotes, currency limits, network fees, and transaction lookups. Sell quotes and sell transaction lookups. Supported countries, currencies, payment methods, and IP-address checks. Token data and token lists for DeFi assets. Programmatic on-ramp and off-ramp via virtual bank accounts. Receive asynchronous notifications when transaction status changes. ## Authentication and base URL See [Using the API](/api-reference/widget/using-the-api) for the base URL, API key types, and which endpoints use which authentication scheme. ## Errors Errors return a JSON body with a stable `moonPayErrorCode`. See [Errors](/api-reference/widget/errors) for the response shape and code catalog. # Update on ramp virtual account Source: https://dev.moonpay.com/api-reference/widget/updateonrampvirtualaccount PATCH /v1/virtual-accounts/onramp/{id} # Using the API Source: https://dev.moonpay.com/api-reference/widget/using-the-api Base URL, API keys, and which widget endpoints need which authentication scheme ## Base URL ```text theme={null} https://api.moonpay.com ``` Test and live environments use the same base URL. The key prefix determines the environment. ## Availability Not every endpoint in this reference is available to every account: | Tier | Meaning | Endpoints | | ------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | Self-serve | Works with your API keys today | Currencies, quotes, limits, countries, transactions, customers, payment methods, DeFi tokens | | Requires enablement | Returns `403` until enabled for your account. Contact support | Virtual accounts | ## Authentication The widget API uses two authentication schemes. Every endpoint page in this reference declares the scheme it needs in its **Authorizations** section. ### API keys You have two kinds of API key, both found on the [API keys page](https://dashboard.moonpay.com/developers/api-keys) of your MoonPay dashboard: | Key | Prefixes | Where it can live | Used as | | --------------- | ---------------------- | ----------------------------- | -------------------------------------------- | | Publishable key | `pk_test_`, `pk_live_` | Client-side code, widget URLs | `apiKey` query parameter | | Secret key | `sk_test_`, `sk_live_` | **Server-side only** | `Authorization: Api-Key ` header | `_test_` keys operate against the sandbox, `_live_` keys against production. Test and live data are fully separate. Never expose a secret key in client-side code, mobile apps, or version control. Anyone holding it can read your customers' transaction data. ### Publishable key (query parameter) Currencies, quotes, limits, network fees, and single-transaction lookups authenticate with your publishable key as the `apiKey` query parameter: ```bash theme={null} curl "https://api.moonpay.com/v3/currencies/btc/buy_quote?apiKey=pk_test_key&baseCurrencyAmount=100&baseCurrencyCode=usd" ``` `GET /v3/countries` works without a key; passing one tailors availability to your account's configuration. ### Secret key (Authorization header) The [server-to-server endpoints](/api-reference/widget/getbuytransactions) and `GET /payments/v1/payment-method-config` authenticate with your secret key in the `Authorization` header, using the `Api-Key` prefix: ```bash curl theme={null} curl "https://api.moonpay.com/v1/transactions" \ --header "Authorization: Api-Key sk_test_key" ``` ```typescript fetch theme={null} const response = await fetch("https://api.moonpay.com/v1/transactions", { headers: { Authorization: "Api-Key sk_test_key" }, }); ``` ### URL signing Widget URLs that carry sensitive query parameters require a `signature` parameter, computed with your secret key. This is separate from API authentication. See [URL signing](/widget/on-ramp/customization/url-signing). # Cancel Sell transaction by externalId Source: https://dev.moonpay.com/api-reference/widget/webhooks/cancelselltransactionbyexternalid DELETE /v3/sell_transactions/ext/{transactionId} Cancels a sell transaction based on their external transaction ID. This endpoint will return HTTP status 204 No Content if the sell transaction was successfully canceled. If sell transaction could not be canceled (e.g. because it has already been completed) it will return HTTP status 409 Conflict. # identity_check_updated Source: https://dev.moonpay.com/api-reference/widget/webhooks/identity-check-updated api-reference/widget/webhooks.openapi.json webhook identity_check_updated Sent whenever the KYC status of a customer changes. # Overview Source: https://dev.moonpay.com/api-reference/widget/webhooks/overview ## When to use webhooks Webhooks are essential for managing behind-the-scenes transactions. They allow you to receive alerts for asynchronous updates to transaction statuses. MoonPay can send webhook events that notify your application whenever an activity occurs on your account. This feature is particularly valuable for tracking changes like transaction status updates, that are not triggered by a direct API request. These notifications are delivered through HTTP POST requests to any endpoint URLs you've specified in your account's [Webhooks settings](https://dashboard.moonpay.com/developers/#webhooks). MoonPay is capable of sending a single event to multiple webhook endpoints. ## Configuring your webhook settings Webhooks are configured in your MoonPay dashboard's [Webhook settings](https://dashboard.moonpay.com/developers/#webhooks). Click Add Endpoint to reveal a form where you can add a new URL for receiving webhooks. Webhooks dashboard You can enter any URL as the destination for events. However, this should be a dedicated page on your server that is set up to receive webhook notifications. You can choose to be notified of all event types, or only specific ones. The dashboard may list event types that do not apply to widget integrations, such as `swap_*` events. These never fire for widget accounts and are safe to leave unselected. Using test or live API keys determines whether test events or live events are sent to your configured URL. If you want to send both live and test events to the same URL, you need to create two separate settings. You can add as many URLs as you like. ## Delivery, retries and ordering Every event is delivered as an HTTP POST request with a JSON body, signed with the `Moonpay-Signature` and `Moonpay-Signature-V2` headers (see [Request signing](/api-reference/widget/webhooks/signature)). * **Acknowledge quickly.** Respond with a 2xx status code within 5 seconds. Any non-2xx response, timeout or connection error counts as a failed delivery attempt. If you need to do heavy processing, acknowledge first and process asynchronously. * **Failed deliveries are retried.** MoonPay retries a failed delivery up to 9 times with exponential backoff, starting at 1 second and roughly doubling per attempt. Once retries are exhausted the event is not redelivered. * **Delivery is at-least-once.** Duplicate deliveries can occur, so make your handler idempotent. For example, deduplicate on the event `type` plus the transaction `id` and `updatedAt` values in the payload. * **Events are not ordered.** Events can arrive out of order, especially when retries are involved. Use the `updatedAt` timestamp inside the payload rather than arrival order to decide whether an event is newer than the state you have stored. ## Receiving a webhook notification Setting up an endpoint to receive webhook HTTP POST requests in the JSON request body can vary based on your backend stack and hosting environment. Below are some methods to achieve this using different technologies: ### Python with Flask Flask is a lightweight WSGI web application framework in Python. ```python theme={null} from flask import Flask, request, jsonify app = Flask(**name**) @app.route('/webhook', methods=['POST']) def webhook(): # Flask automatically parses JSON if the Content-Type is application/json data = request.json print(f"Received data: {data}") return jsonify({"status": "success"}), 200 if **name** == '**main**': app.run(port=5000) ``` ### Node.js with Express Node.js is widely used for server-side development, and Express is one of the most popular frameworks for Node.js. ```typescript theme={null} const express = require("express"); const bodyParser = require("body-parser"); const app = express(); // Middleware to parse JSON payload from incoming POST request app.use(bodyParser.json()); app.post("/webhook", (req, res) => { // req.body contains the parsed JSON payload const data = req.body; console.log(`Received data: ${JSON.stringify(data)}`); res.status(200).json({ status: "success" }); }); app.listen(3000, () => { console.log("Server started on "); }); ``` ### Ruby with Sinatra Sinatra is a DSL (Domain Specific Language) for quickly creating web applications in Ruby with minimal effort. ```ruby theme={null} require 'sinatra' require 'json' post '/webhook' do # Reading and parsing the JSON payload from the request body data = JSON.parse(request.body.read) puts "Received data: #{data}" [200, { 'Content-Type' => 'application/json' }, { status: 'success' }.to_json] end ``` ### Deployment After setting up your webhook endpoint, you'll need to deploy it. You can use cloud services like AWS, Google Cloud, Heroku, or any VPS provider for this purpose. ### Secure your webhook You may validate incoming requests to ensure they are coming from a trusted source. Visit our [webhooks signature](/api-reference/widget/webhooks/signature) for more information. This is a recommended best practice. ### Test your webhook After deployment, you can test your webhook endpoint using Postman or curl to send a simulated POST request. # sell_transaction_created Source: https://dev.moonpay.com/api-reference/widget/webhooks/sell-transaction-created api-reference/widget/webhooks.openapi.json webhook sell_transaction_created Sent when a customer creates a Sell transaction in the widget. # sell_transaction_failed Source: https://dev.moonpay.com/api-reference/widget/webhooks/sell-transaction-failed api-reference/widget/webhooks.openapi.json webhook sell_transaction_failed Sent when a Sell transaction fails. # sell_transaction_requote_required Source: https://dev.moonpay.com/api-reference/widget/webhooks/sell-transaction-requote-required api-reference/widget/webhooks.openapi.json webhook sell_transaction_requote_required Sent when a Sell transaction requires a requote because the price moved before the customer's deposit was received. # sell_transaction_updated Source: https://dev.moonpay.com/api-reference/widget/webhooks/sell-transaction-updated api-reference/widget/webhooks.openapi.json webhook sell_transaction_updated Sent whenever a Sell transaction's status or details change. # Request signing Source: https://dev.moonpay.com/api-reference/widget/webhooks/signature ## Checking a webhook signature MoonPay signs the webhook events and requests we send to your endpoints. We do so by including a signature in each event’s `Moonpay-Signature-V2` header. This allows you to validate that the events and requests were sent by MoonPay, not by a third party. Before you can verify `Moonpay-Signature-V2` signatures for webhook events, you need to retrieve your webhook API key from the [Developers page](https://dashboard.moonpay.com/developers/) on the MoonPay dashboard. The `Moonpay-Signature-V2` header contains a timestamp and one signature. The timestamp is prefixed by t=, and the signature is prefixed by s=. ```bash bash Moonpay-Signature-V2: theme={null} t=1492774577,s=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd ``` MoonPay generates signatures using a hash-based message authentication code ([HMAC](https://en.wikipedia.org/wiki/HMAC)) with [SHA-256](https://en.wikipedia.org/wiki/SHA-2). Split the header, using the , character as the separator, to get a list of elements. Then split each element, using the = character as the separator, to get a prefix and value pair. The value for the prefix `t` corresponds to the timestamp, and `s` corresponds to the signature. You achieve this by concatenating: * The timestamp (as a string) * The character . and * For a `POST` request, the actual JSON payload (i.e., the request's body). For a `GET` request, the search string (e.g., ?externalCustomerId=adbb317d-cde9-4ebb-93a3-1b271812de06). Compute a HMAC with the SHA-256 hash function. Use your account's webhook API key as the key, and use the `signed_payload` string as the message in both cases. Compare the signature in the header to the expected signature. # transaction_created Source: https://dev.moonpay.com/api-reference/widget/webhooks/transaction-created api-reference/widget/webhooks.openapi.json webhook transaction_created Sent when a customer creates a Buy transaction in the widget. # transaction_failed Source: https://dev.moonpay.com/api-reference/widget/webhooks/transaction-failed api-reference/widget/webhooks.openapi.json webhook transaction_failed Sent when a Buy transaction fails. # transaction_updated Source: https://dev.moonpay.com/api-reference/widget/webhooks/transaction-updated api-reference/widget/webhooks.openapi.json webhook transaction_updated Sent whenever a Buy transaction's status or details change. # virtual_account_status_updated Source: https://dev.moonpay.com/api-reference/widget/webhooks/virtual-account-status-updated api-reference/widget/webhooks.openapi.json webhook virtual_account_status_updated Sent when the status of a virtual account changes. # virtual_account_transaction_status_updated Source: https://dev.moonpay.com/api-reference/widget/webhooks/virtual-account-transaction-status-updated api-reference/widget/webhooks.openapi.json webhook virtual_account_transaction_status_updated Sent when the status of a transaction within a virtual account changes. # Changelog Source: https://dev.moonpay.com/platform/changelog Updates to the MoonPay developer platform **Challenge frame `clientToken` in manual integrations.** The [Challenge frame](/platform/frames/challenge) reference said `clientToken` is included in the URL automatically. That's only true when the SDK opens the frame for you. If you build the challenge URL yourself, append `clientToken` the same way you already append `channelId`, before setting it as the frame `src`. Updated references: * [Challenge frame](/platform/frames/challenge) * [Manual integration (Web)](/platform/guides/manual-integration/web#challenge-handling) * [Manual integration (Android)](/platform/guides/manual-integration/android#challenge-handling) * [Manual integration (iOS)](/platform/guides/manual-integration/ios#challenge-handling) * [Manual integration (React Native)](/platform/guides/manual-integration/react-native#challenge-component) * [Manual integration (Flutter)](/platform/guides/manual-integration/flutter#challenge-widget) **Guest checkout limit upgrade.** A guest checkout customer who quotes for more than their spending limit can now raise that limit with their date of birth and the last four digits of their Social Security number, without completing full verification. The buy quote returns `executable: false` plus a `challenge` object when the customer is eligible. Render the challenge frame at `challenge.url`, then request the quote again. The frame runs a new `guest_checkout_limit_upgrade` flow and emits `complete` with a `status` of `upgraded` or `rejected`. Needs `@moonpay/platform-sdk-web` or `@moonpay/platform-sdk-react-native` 1.15.2 or later, and the capability enabled on your account. Updated references: * [Guest checkout](/platform/guides/guest-checkout#upgrade-a-guest-account) * [Handle challenges](/platform/guides/handling-challenges) * [Challenge frame](/platform/frames/challenge) * [client.setupChallenge()](/platform/sdk-reference/web/setup-challenge) * [MoonPayChallenge](/platform/sdk-reference/react-native/components/moonpay-challenge) * [client.getQuote()](/platform/sdk-reference/web/get-quote) * [Quote](/api-reference/platform/objects-and-types/quote) **Bank transfers on the Platform SDK.** You can now accept bank-transfer payments (SEPA for EUR) through the same headless Buy frame you use for cards. Quote with `paymentMethod.type` set to `"sepa"`, open the frame, and read the transaction `id` from the `complete` event. The transaction then carries a `bankTransferDepositInfo` object with the account details and payment reference. Your app renders those details natively; MoonPay does not render the deposit UI. Poll `getTransaction` for status. Bank transfers use floating pricing: quotes are estimates until the customer's funds settle. The quote now carries an `exchangeRateType` enum (`"floating"` for bank transfers, `"fixed"` for card and wallet methods) that pairs with the `exchangeRate` field. When `exchangeRateType` is `"floating"`, render the estimated crypto amount with a tilde (`~`) and tell the customer the final amount is set at settlement. Bank transfers don't support DeFi assets — a bank-transfer quote for a `caip19` destination fails with `400`. Offer a card or wallet payment method for those assets instead. New pages: * [Pay with bank transfer](/platform/guides/pay-with-bank-transfer) Updated references: * [client.getQuote() (React Native)](/platform/sdk-reference/react-native/get-quote#exchange-rate-type) * [client.getTransaction() (React Native)](/platform/sdk-reference/react-native/get-transaction#bank-transfer-deposit-details) * [client.setupBuy() (React Native)](/platform/sdk-reference/react-native/setup-buy#bank-transfer) * [client.getPaymentMethods() (React Native)](/platform/sdk-reference/react-native/get-payment-methods) * [client.getQuote() (Web)](/platform/sdk-reference/web/get-quote#exchange-rate-type) * [client.getTransaction() (Web)](/platform/sdk-reference/web/get-transaction#bank-transfer-deposit-details) * [client.setupBuy() (Web)](/platform/sdk-reference/web/setup-buy#bank-transfer) * [client.getPaymentMethods() (Web)](/platform/sdk-reference/web/get-payment-methods) * [Choose a payment method](/platform/guides/payment-methods#compare-payment-methods) * [Quote object](/api-reference/platform/objects-and-types/quote#exchange-rate-type) * [Transaction object](/api-reference/platform/objects-and-types/transaction#bank-transfer-deposit-info) * [Payment Method object](/api-reference/platform/objects-and-types/payment-method#payment-method-types) *** **Simulate bank-transfer settlement in test mode.** Bank-transfer buy transactions (SEPA for EUR) settle asynchronously when the customer's deposit arrives, which never happens in test mode, so a `pending` transaction could not complete without sending real money. A new sandbox-only endpoint drives a bank-transfer transaction to a terminal state without real funds. Choose an `outcome`: `settled` completes the transaction, or `timeout` fails it exactly as a real 7-day timeout does. Settlement is asynchronous, so poll `getTransaction` for the terminal state. New pages: * [Simulate bank-transfer settlement](/api-reference/platform/endpoints/transactions/simulate-bank-transfer) Updated references: * [Test mode](/platform/overview/test-mode#bank-transfers) **Guest checkout with Google Pay.** Guest checkout now covers Apple Pay and Google Pay. New customers buy with either wallet before they have a MoonPay account. Recognition still uses `capabilities.guestCheckout`. Google Pay uses `setupGooglePay` and `/platform/v1/google-pay`. Second-factor and KYC step-up both surface as the same `challenge` event; a limit-exceeded purchase fails with `failureCode: "transactionNotAllowed"`. Updated references: * [Guest checkout](/platform/guides/guest-checkout) * [Pay with Google Pay](/platform/guides/pay-with-google-pay) * [Google Pay frame](/platform/frames/google-pay) * [Apple Pay frame](/platform/frames/apple-pay) * [Handle challenges](/platform/guides/handling-challenges) * [Choose a payment method](/platform/guides/payment-methods) **Frame handshake failures are no longer silent.** If a frame's handshake ack comes from an origin that isn't on your allowlist, the frame now sends a `generic` error and closes the channel instead of leaving you waiting on a handshake that never completes. Updated references: * [Frames overview](/platform/frames/overview#lifecycle) **Supported currencies and MoonPay Gateway overview.** A new Supported currencies page brings together MoonPay's on-ramp and off-ramp currency table with an overview of MoonPay Gateway, including its benefits, purchase flow, and support for DeFi tokens on Solana, Ethereum, Base, and HyperCore. New pages: * [Supported currencies](/platform/overview/supported-currencies) **Payment presentation and fee behavior clarified.** Going Live requirements now distinguish the contextual quote summary from the required fee breakdown across Apple Pay, Google Pay, cards, the buy button, and the widget. Core concepts now includes an illustrative side-by-side fee-behavior comparison. Updated references: * [Going Live](/platform/overview/going-live) * [Core concepts](/platform/overview/core-concepts#fee-behavior) * [Pay with Apple Pay](/platform/guides/pay-with-apple-pay) * [Pay with Google Pay](/platform/guides/pay-with-google-pay) * [Pay with card](/platform/guides/pay-with-card) * [Pay with the buy button](/platform/guides/pay-with-buy-button) * [Pay with widget](/platform/guides/pay-with-widget) *** **Verification tiers documented.** A new [Verification tiers](/platform/guides/verification-tiers) page is the public reference for what customers complete at each KYC tier, per region: the steps each tier adds, the regional tier ladders, and how higher tiers unlock higher purchase limits. New pages: * [Verification tiers](/platform/guides/verification-tiers) **DeFi tokens on the Platform API.** You can now discover and buy DeFi tokens through Gateway. The new list assets endpoint returns both CeFi and DeFi tokens, each with its CAIP-19 identifier and on-chain metadata. To quote a DeFi token, pass its `caip19` as the destination instead of a `code`, since a DeFi token's `code` is not unique. DeFi buys also surface the `gateway-token` payment disclosure documented in Going Live. New pages: * [List assets](/api-reference/platform/endpoints/assets/list) Updated references: * [Asset](/api-reference/platform/objects-and-types/asset) * [Quote](/api-reference/platform/objects-and-types/quote) * [Get a quote](/api-reference/platform/endpoints/quotes/get) * [Going Live](/platform/overview/going-live) *** **`blocks.moonpay.com` references updated to `platform.moonpay.com`.** Frame URLs, iframe/WebView `src` examples, and reference implementation snippets that still pointed at the deprecated `blocks.moonpay.com` domain now use `platform.moonpay.com`, matching the SDK's `DEFAULT_FRAME_BASE_URL`. Updated references: * [Widget frame](/platform/frames/widget) * [Buy frame](/platform/frames/buy) * [Buy button frame](/platform/frames/buy-button) * [Apple Pay frame](/platform/frames/apple-pay) * [Google Pay frame](/platform/frames/google-pay) * [Add Card frame](/platform/frames/add-card) * [Connect frame](/platform/frames/connect) * [Check frame](/platform/frames/check) * [Reset frame](/platform/frames/reset) * [Configure frame appearance](/platform/guides/presentation-and-appearance) * [Handle challenges](/platform/guides/handling-challenges) * [Test mode](/platform/overview/test-mode) * [Manual integration — Web](/platform/guides/manual-integration/web) * [Manual integration — React Native](/platform/guides/manual-integration/react-native) * [Manual integration — Flutter](/platform/guides/manual-integration/flutter) * [Manual integration — Android](/platform/guides/manual-integration/android) * [Manual integration — iOS](/platform/guides/manual-integration/ios) * [SDK reference implementation — Web](/platform/sdk-reference/reference-implementation/web) * [SDK reference implementation — React Native](/platform/sdk-reference/reference-implementation/react-native) * [SDK reference implementation — Flutter](/platform/sdk-reference/reference-implementation/flutter) *** **`gateway-token` disclosure described in the API reference.** The [Get a quote](/api-reference/platform/endpoints/quotes/get) endpoint reference now describes the `gateway-token` disclosure id inline with the other disclosures, matching the [Going Live](/platform/overview/going-live) copy. Updated references: * [Get a quote](/api-reference/platform/endpoints/quotes/get) *** **Manual integration guides accept `externalTransactionId`.** The buy, Apple Pay, Google Pay, and widget frame examples across every platform's manual integration guide now take an optional `externalTransactionId` and forward it as an initialization parameter, matching the [buy](/platform/frames/buy), [Apple Pay](/platform/frames/apple-pay), [Google Pay](/platform/frames/google-pay), and [widget](/platform/frames/widget) frame references. Updated references: * [Manual integration — Web](/platform/guides/manual-integration/web) * [Manual integration — Android](/platform/guides/manual-integration/android) * [Manual integration — iOS](/platform/guides/manual-integration/ios) * [Manual integration — React Native](/platform/guides/manual-integration/react-native) * [Manual integration — Flutter](/platform/guides/manual-integration/flutter) **Device and browser support for Apple Pay and Google Pay.** The Pay with Apple Pay and Pay with Google Pay guides now have Device and browser support sections. They cover which browsers and devices each payment method works on, what embedding the frame in a native app requires (`WKWebView` dialog handling on iOS, Payment Request API setup in Android WebView), and runtime detection through the frame's `unsupported` event. The Apple Pay guide also documents that the Apple Pay frame doesn't offer Apple's cross-device flow, where the customer scans a QR code with their iPhone, and that the widget does. Updated references: * [Pay with Apple Pay](/platform/guides/pay-with-apple-pay) * [Pay with Google Pay](/platform/guides/pay-with-google-pay) *** **`apple_pay` quotes render Apple Pay in the widget.** The Pay with widget guide no longer says an `apple_pay` quote renders the card form. The widget now renders Apple Pay for `apple_pay` quotes, including the cross-device QR flow in browsers the headless frame doesn't support. Updated references: * [Pay with widget](/platform/guides/pay-with-widget) *** **Supported testnet list corrected.** Solana test transactions run on Devnet, not Testnet, and Binance Coin is no longer available in test mode. Bitcoin Cash and the XRP Ledger (`XRP` and `RLUSD`) are supported and now documented. The [test assets](/platform/overview/test-mode#test-assets) table also lists asset codes, links the [MoonPayToken](https://sepolia.etherscan.io/address/0x699cfe8997d647d03325ef4bfd039d5bb0984a17) contract that ERC-20 test transfers deliver in place of the real token, and documents that test-mode purchases deliver 1/100th of the quoted amount. Updated references: * [Test mode](/platform/overview/test-mode#test-assets) * [Sandbox testing guide](/widget/sandbox-testing#sandbox-token-and-testnet-support) * [Widget FAQs](/widget/faqs#which-currencies-does-the-sandbox-support) **Payment disclosure IDs documented.** The `paymentDisclosures[].id` field on a buy quote now lists every known disclosure identifier and what each one means, instead of an undocumented plain string. Updated references: * [Get a quote](/api-reference/platform/endpoints/quotes/get) * [Quote](/api-reference/platform/objects-and-types/quote) *** **KYC data requirements and the Auth frame are now in the sidebar.** The per-country [KYC data requirements](/platform/guides/kyc-data-requirements) reference now appears in the Customers group and is indexed for search, and the [Auth frame](/platform/frames/auth) — the entry point for onboarding via API — now appears under Frames → Connections. Updated references: * [KYC data requirements](/platform/guides/kyc-data-requirements) * [Auth](/platform/frames/auth) *** **Check frame documentation matches the SDK.** The [Check frame](/platform/frames/check) now documents the `skipKyc` initialization parameter used by Customer API integrations and the `termsAcceptanceRequired` connection status, matching the SDK's `Connection` union. The [Hosted onboarding](/platform/guides/connect-a-customer) guide's connection statuses section covers `termsAcceptanceRequired` too. Updated references: * [Check](/platform/frames/check) * [Hosted onboarding](/platform/guides/connect-a-customer#connection-statuses) *** **Live API data called out as canonical.** Pages that enumerate per-country requirements or regional availability now carry a callout pointing to the live API response — `kyc.requirements` on the customer, capabilities on the connection — as the definitive answer for a given customer. Updated references: * [KYC data requirements](/platform/guides/kyc-data-requirements) * [Onboarding via API](/platform/guides/customer-api) * [Guest checkout](/platform/guides/guest-checkout) * [Choose a payment method](/platform/guides/payment-methods) **Per-partner capability enablement documented.** Some Platform API capabilities must be enabled on your account by MoonPay before you can call them. Requests to an endpoint for a capability that is not enabled fail with a plain `404` (`Cannot POST /...`) rather than a permissions error. The API reference now calls this out in a new [Capability enablement](/api-reference/platform/documentation/using-the-api#capability-enablement) note at the top of the Using the Platform API page, the introduction mentions it, and every Platform API endpoint reference page carries a short reminder. Updated references: * [Using the Platform API](/api-reference/platform/documentation/using-the-api) * [Introduction](/platform/overview/introduction) * [Platform API reference](/api-reference/platform) *** **Leftover preview wording removed.** The account setup callout on the requirements and credentials pages no longer refers to the preview program. Updated references: * [Requirements](/platform/overview/requirements) * [API and SDK credentials](/platform/guides/api-and-sdk-credentials) **Pay with widget and payment method type corrections.** The Pay with widget guide no longer hardcodes a list of supported `paymentMethod.type` values — several were incorrect — and its example now uses the correct `card` type. The guide and the SDK reference pages for quotes and payment methods now point to the [Get a quote API reference](/api-reference/platform/endpoints/quotes/get), which renders the enum from the live OpenAPI spec, as the single source of truth. The guide and the `setupWidget` SDK reference also state that the widget requires an executable quote — pass both a `paymentMethod` and a wallet — and that a non-executable quote will not render. The widget collects payment for the method you pass; it does not present payment-method selection. Updated references: * [Pay with widget](/platform/guides/pay-with-widget) * [client.setupWidget() (Web)](/platform/sdk-reference/web/setup-widget) * [client.setupWidget() (React Native)](/platform/sdk-reference/react-native/setup-widget) * [getQuote() (Web)](/platform/sdk-reference/web/get-quote) * [getQuote() (React Native)](/platform/sdk-reference/react-native/get-quote) * [getPaymentMethods() (Web)](/platform/sdk-reference/web/get-payment-methods) * [getPaymentMethods() (React Native)](/platform/sdk-reference/react-native/get-payment-methods) * [`` (React Native)](/platform/sdk-reference/react-native/components/moonpay-widget) **Provide the React Native session token after mount.** The `sessionToken` prop on `` is now optional. Mount the provider without it and call `initialize()` from `useMoonPay()` once your server returns the token — handy when you fetch it after the customer signs in. A new `isInitialized` flag reports when the SDK has a token; connection methods called before then return an `err()` result. Passing the `sessionToken` prop still works unchanged. Updated references: * [React Native — `MoonPayProvider`](/platform/sdk-reference/react-native/provider) * [React Native — `useMoonPay()`](/platform/sdk-reference/react-native/use-moonpay) *** **Widget frame example accepts `externalTransactionId`** — the manual web integration guide's widget frame example now takes an optional `externalTransactionId` and forwards it as an initialization parameter, matching the buy frame example in the same guide and the [widget frame](/platform/frames/widget) reference. Updated references: * [Manual integration — Web](/platform/guides/manual-integration/web) **Customer API in the API reference.** The `/platform/v1/customers` endpoints now have dedicated API reference pages: get a customer, submit KYC data, get a file upload URL, confirm uploaded files, and export customer data. Two new type pages document the Customer object (including KYC status and requirements) and the customer export payload. New pages: * [Get a customer](/api-reference/platform/endpoints/customers/get) * [Submit KYC data](/api-reference/platform/endpoints/customers/submit-kyc) * [Get a file upload URL](/api-reference/platform/endpoints/customers/get-upload-url) * [Confirm uploaded files](/api-reference/platform/endpoints/customers/submit-files) * [Export customer data](/api-reference/platform/endpoints/customers/export) * [Customer](/api-reference/platform/objects-and-types/customer) * [Customer export](/api-reference/platform/objects-and-types/customer-export) *** **Due-diligence questionnaires and reporting currency.** The KYC data requirements reference now documents the `questionnaires` requirement category: the Customer Due Diligence and Enhanced Due Diligence answer fields, and the monetary-amount shape they share. Monetary answers such as `grossAnnualIncome`, `expectedTransactionAmountPerMonth`, and `netWorth` are denominated in the reporting currency for the customer's `residentialAddress.country`: GBP for GBR, AUD for AUS and NZL, EUR for EEA countries, and USD everywhere else. The API rejects a mismatched `currency` with a 400 validation error. The Onboarding via API guide now shows how to submit questionnaire answers. *** **`requiredFields` on requirement entries.** The Onboarding via API guide now documents which requirement categories populate `requiredFields` when incomplete — `basicDetails`, `residentialAddress`, `taxIdentifiers`, and `questionnaires` — and its get-a-customer example shows the `questionnaires` entry naming the outstanding questionnaire types. Updated references: * [KYC data requirements](/platform/guides/kyc-data-requirements) * [Onboarding via API](/platform/guides/customer-api) *** **Terms acceptance — new guide for the API-driven path.** Partners on the API-driven onboarding path must present MoonPay's Terms of Use and Privacy Policy in their own UI before the customer's first transaction. The new guide covers the two presentation methods, recording acceptance with `termsAcceptedAt` when creating a session, handling `termsAcceptanceRequired`, re-presenting the terms when they change, and the related biometric-consent and phone-verification steps. New pages: * [Terms acceptance](/platform/guides/terms-acceptance) Updated references: * [Onboarding via API](/platform/guides/customer-api) * [Guest checkout](/platform/guides/guest-checkout) * [Auth frame](/platform/frames/auth) * [Web SDK — `getConnection()`](/platform/sdk-reference/web/get-connection) * [React Native — `getConnection()`](/platform/sdk-reference/react-native/get-connection) * [KYC data requirements](/platform/guides/kyc-data-requirements) * [Going live](/platform/overview/going-live) **`externalTransactionId` on the buy widget.** The widget frame now accepts an optional `externalTransactionId` — a partner-assigned identifier for the transaction attempt, useful for reconciliation. It matches the field already available on the buy, Google Pay, Apple Pay, and buy-button frames. `setupWidget()` forwards it, and on the React Native `` component the same value is exposed as a mount-time prop. Updated references: * [Widget frame](/platform/frames/widget) * [Web SDK — `setupWidget()`](/platform/sdk-reference/web/setup-widget) * [React Native — `client.setupWidget()`](/platform/sdk-reference/react-native/setup-widget) * [React Native — ``](/platform/sdk-reference/react-native/components/moonpay-widget) **`externalTransactionId` on payment-button setup.** The Apple Pay and buy button setup methods now accept an optional `externalTransactionId` — a partner-assigned identifier for the transaction attempt, useful for reconciliation. It matches the field already available on Google Pay. On the React Native `` and `` components the same value is exposed as a mount-time prop. Updated references: * [Web SDK — `setupApplePay()`](/platform/sdk-reference/web/setup-apple-pay) * [Web SDK — `setupBuyButton()`](/platform/sdk-reference/web/setup-buy-button) * [React Native — ``](/platform/sdk-reference/react-native/components/moonpay-apple-pay-button) * [React Native — ``](/platform/sdk-reference/react-native/components/moonpay-buy-button) **Platform guides reorganized around onboarding paths.** The Platform tab now groups guides into Onboard customers, Accept payments, and Integration essentials. A new decision page explains the three ways customers can onboard: the hosted connect flow, the Customer API, or guest checkout. The Customer API guide now documents the Auth frame entry path (`getConnection({ skipKyc: true })` + `setupAuth()`) instead of routing through the hosted connect flow. Customer export moved to its own page, and the per-country KYC data requirements reference is now in the sidebar. New pages: * [Choose an onboarding path](/platform/guides/onboarding-paths) * [Choose a payment method](/platform/guides/payment-methods) * [Export customer data](/platform/guides/export-customer-data) * [KYC data requirements](/platform/guides/kyc-data-requirements) Updated references: * [Onboarding via API](/platform/guides/customer-api) * [Guest checkout](/platform/guides/guest-checkout) * [Core concepts](/platform/overview/core-concepts) * [Handle challenges](/platform/guides/handling-challenges) **`buttonPressed` frame event.** The Apple Pay, Google Pay, and Buy Button frames now emit a `buttonPressed` event the instant the customer taps the native pay button, before the OS presents the payment sheet (PassKit on iOS, the Google Pay sheet on Android). It is an intent-to-buy signal with no payload, routed by `channelId`. It is distinct from `complete`: it fires earlier and unconditionally on tap, and still fires if the customer opens the payment sheet and then cancels. Listen for `complete` for the transaction outcome. Updated references: * [Apple Pay frame](/platform/frames/apple-pay) * [Google Pay frame](/platform/frames/google-pay) * [Buy button frame](/platform/frames/buy-button) *** **`feeBehavior` on buy quotes.** The buy-quote endpoint accepts an optional `feeBehavior` field, `"inclusive"` or `"exclusive"`, that controls how fees relate to `source.amount`. With `"inclusive"` (the default) the customer pays exactly `source.amount` and fees are carved out of it. With `"exclusive"` fees are added on top, so the full `source.amount` is converted and the customer receives more crypto. It applies only when you quote by `source.amount`; quotes by `destination.amount` are always fees-inclusive. The response echoes the effective `feeBehavior`. Omitting the field keeps the existing behavior. Updated references: * [Quotes concepts](/platform/overview/core-concepts#quotes) * [Quote object](/api-reference/platform/objects-and-types/quote) * [Web SDK — `getQuote()`](/platform/sdk-reference/web/get-quote) * [React Native SDK — `getQuote()`](/platform/sdk-reference/react-native/get-quote) *** **Identity SDK methods marked deprecated.** The web SDK's identity methods (`createIdentity`, `getIdentity`, `updateIdentity`, `verifyIdentity`, `getIdentityUploadUrl`, `submitIdentityFiles`) are deprecated in favor of the customerId-keyed Customer API. Each method's reference now links to its Customer API replacement. Updated references: * [Web SDK — Identity methods](/platform/sdk-reference/web/identity) * [Customer API](/platform/guides/customer-api) **Terms acceptance instructions corrected for headless partners.** The `termsAcceptanceRequired` handling docs referenced `POST /platform/v1/terms/attestations`, an endpoint that no longer exists. The documented flow is now to capture the acceptance timestamp in your own UI, pass it as `termsAcceptedAt` when you create a new session, and relaunch the flow. This requires the Identity or Guest Checkout account capability. Updated references: * [Auth frame](/platform/frames/auth) * [Web SDK — `getConnection()`](/platform/sdk-reference/web/get-connection) * [React Native SDK — `getConnection()`](/platform/sdk-reference/react-native/get-connection) *** **Consent capture flow documented for customer export.** The Customer API guide's export section now covers how partners capture a customer's consent with the web SDK's `setupCustomerExport()`. Updated references: * [Customer API](/platform/guides/customer-api) **Customer API guide added** — new guide for the customerId-keyed Customer API: getting a customer's KYC status, submitting outstanding requirements, uploading identity files, verifying a customer, and exporting a customer's verified identity to another system with their consent. New pages: * [Customer API](/platform/guides/customer-api) **Conflict signal on connection check** — the check frame's `complete` event now includes an optional `mismatch` field on the `connectionRequired` payload. It is present and `true` when the session's email and phone number resolve to two different MoonPay customers, and absent when there is no conflict — never `false`. Use it to route the customer through the connect flow proactively, before rendering payment UI such as Apple Pay, instead of discovering the conflict at purchase time. The `CustomerCapabilities` documentation is also corrected: `capabilities.oneTapApplePay` was removed upstream and is replaced by `capabilities.guestCheckout`, which is present when guest checkout is enabled for the partner and the session is a guest-checkout session. Updated references: * [Check frame](/platform/frames/check) * [Connect frame](/platform/frames/connect) * [Web SDK — `getConnection()`](/platform/sdk-reference/web/get-connection) * [React Native SDK — `getConnection()`](/platform/sdk-reference/react-native/get-connection) * [Connect a customer](/platform/guides/connect-a-customer) * [Manual integration — Web](/platform/guides/manual-integration/web) * [Manual integration — React Native](/platform/guides/manual-integration/react-native) * [Manual integration — Flutter](/platform/guides/manual-integration/flutter) * [Manual integration — Android](/platform/guides/manual-integration/android) * [Manual integration — iOS](/platform/guides/manual-integration/ios) **WebView and sandboxed iframe embedding documented** — the Google Pay and Apple Pay frame reference pages are now the source of truth for embedding these frames in a WebView or sandboxed iframe. The Google Pay frame documents the Android WebView requirements for Payment Request API setup and the `sandbox` attribute values you need when embedding in a sandboxed iframe for PCI DSS v4 compliance. The Apple Pay frame documents the WKWebView requirement to handle JavaScript dialogs. The Android and iOS manual-integration guides point to these frame pages. Updated references: * [Google Pay frame](/platform/frames/google-pay) * [Apple Pay frame](/platform/frames/apple-pay) * [Android manual integration](/platform/guides/manual-integration/android) * [iOS manual integration](/platform/guides/manual-integration/ios) *** **Guest checkout with Apple Pay** — new guide for letting customers buy crypto with Apple Pay before they have a MoonPay account. It covers creating a session with the customer's identity, detecting the `guestCheckout` capability, executing the purchase, handling verification challenges, and upgrading a guest account. New pages: * [Guest checkout with Apple Pay](/platform/guides/guest-checkout) **Reset frame accepts `clientToken`** — the [Reset frame](/platform/frames/reset) reference now documents `clientToken` (from the connect flow) alongside `apiKey`. Either one authorizes your domain to embed the frame via the `frame-ancestors` Content Security Policy; without one the frame only loads on MoonPay-owned origins. `channelId` remains the only required parameter. **Reset frame parameters corrected** — removed the `language` parameter from the [Reset frame](/platform/frames/reset) reference. The frame is headless and renders no UI, so the parameter had no effect. **HYPE sustainability indicators added** — added Hyperliquid (HYPE) to the [Sustainability transparency](/widget/sustainability-transparency) page with MiCA sustainability indicators provided by CCRI. **Test payment cards updated** — revised the test card tables on the [Test mode](/platform/overview/test-mode) page. Removed all American Express test cards, since Amex is not a supported payment method, and removed a duplicate card entry that appeared in more than one table. **Frame theme parameter documented** — frames accept a `theme` query parameter set to `dark` or `light` to force a specific appearance. Omit it and the frame follows the user's system appearance. The parameter is now documented across the frames that render UI. Updated references: * [Widget frame](/platform/frames/widget) * [Add card frame](/platform/frames/add-card) * [Apple Pay frame](/platform/frames/apple-pay) * [Google Pay frame](/platform/frames/google-pay) * [Challenge frame](/platform/frames/challenge) **Buy Button documentation** — the buy button now has a full guide and frame reference. The buy button renders a MoonPay-hosted express-checkout payment button (Apple Pay, Google Pay, or card) and runs the same buy pipeline as the headless buy frame. New pages: * [Pay with the buy button](/platform/guides/pay-with-buy-button) — guide * [Buy button frame](/platform/frames/buy-button) — frame reference The [`setupBuyButton()` reference](/platform/sdk-reference/web/setup-buy-button) now documents the `ready` event, which the SDK emits once the button is rendered and ready for the customer to tap. *** **Credentials payload documented** — the decrypted shape of the `credentials` string returned by the check and connect frames is now explicitly documented. Once decrypted, `credentials` is a JSON object with `accessToken`, `clientToken`, and `expiresAt`. See [API and SDK credentials](/platform/guides/api-and-sdk-credentials#client-credentials). **Customer geo fields on connection** — `country`, `administrativeArea`, and `area` are now returned directly on the `customer` object in the `complete` event payload (connect and check frames) and in the `Connection` type returned by `getConnection()`. Use these fields to determine which payment disclosures apply for the customer's jurisdiction. `capabilities.ramps.requirements.paymentDisclosures` is deprecated. Read geography from `customer.country`, `customer.administrativeArea`, and `customer.area` instead. Updated references: * [Connect frame](/platform/frames/connect) * [Check frame](/platform/frames/check) * [Web SDK — `getConnection()`](/platform/sdk-reference/web/get-connection) * [React Native SDK — `getConnection()`](/platform/sdk-reference/react-native/get-connection) * [Manual integration — Web](/platform/guides/manual-integration/web) * [Manual integration — React Native](/platform/guides/manual-integration/react-native) * [Manual integration — Flutter](/platform/guides/manual-integration/flutter) * [Manual integration — Android](/platform/guides/manual-integration/android) * [Manual integration — iOS](/platform/guides/manual-integration/ios) *** **React Native SDK is now available** — the [React Native SDK](/platform/sdk-reference/react-native/overview) ([`@moonpay/platform-sdk-react-native`](https://www.npmjs.com/package/@moonpay/platform-sdk-react-native)) is published on npm and its reference is now in the Platform sidebar: * **Provider and hook** — [``](/platform/sdk-reference/react-native/provider) and [`useMoonPay()`](/platform/sdk-reference/react-native/use-moonpay) give any descendant component access to the client. * **Client methods** — customer connection, [email/OTP auth](/platform/sdk-reference/react-native/setup-auth), payment methods, quotes, frame setup (widget, buy, buy button, add card, Apple Pay, Google Pay, challenge), and transactions — mirroring the [Web SDK](/platform/sdk-reference/web/overview) surface. * **[Inline components](/platform/sdk-reference/react-native/components/overview)** — every frame also ships as a declarative component (``, ``, ``, and more) you render directly in your layout, with a reactive `quote` prop that updates live frames without remounting. These replace the deprecated `setupApplePay()`, `setupGooglePay()`, and `setupBuyButton()` client methods. The [SDK reference overview](/platform/sdk-reference/overview), [Web SDK overview](/platform/sdk-reference/web/overview), and [manual integration](/platform/guides/manual-integration/overview) pages now point React Native integrators at the SDK instead of a direct frame integration. *** **Web SDK reference — corrections and 1.0.0 coverage** — the [Web SDK](/platform/sdk-reference/web/overview) docs were audited against the `@moonpay/platform-sdk-web` 1.0.0 source and corrected: * **`getQuote()` input shape** — every example and parameter table now uses the nested request shape the SDK and API accept (`source: { asset: { code }, amount }`, `destination: { asset: { code } }`, `wallet: { address, tag? }`, `paymentMethod: { type, id? }`). The previously documented flat shape (`source: "USD"`, `sourceAmount`, `walletAddress`, string `paymentMethod`) is rejected by the API. Corrected across the [SDK reference](/platform/sdk-reference/web/get-quote), all `pay-with-*` guides, and the [introduction](/platform/overview/introduction). * **New: [`setupAuth()`](/platform/sdk-reference/web/setup-auth)** — the lighter-weight email/OTP counterpart to `connect()` for headless and Identity API integrations, added in SDK 1.0.0. * **New: [Identity methods](/platform/sdk-reference/web/identity)** — `createIdentity`, `getIdentity`, `updateIdentity`, `verifyIdentity`, `getIdentityUploadUrl`, and `submitIdentityFiles`, added in SDK 1.0.0. * **[`getConnection()`](/platform/sdk-reference/web/get-connection)** — now documents the optional `skipKyc` flag for headless integrations. * **[`setupChallenge()`](/platform/sdk-reference/web/setup-challenge)** — the challenge URL no longer needs to carry a `channelId` query parameter; the SDK generates one automatically (SDK 1.0.0). Identity verification challenge URLs can be passed straight through. * **[`setupAddCard()`](/platform/sdk-reference/web/setup-add-card)** — the Add Card frame does emit a `ready` event; the docs previously said it didn't. * **[`setupApplePay()`](/platform/sdk-reference/web/setup-apple-pay) and [`setupGooglePay()`](/platform/sdk-reference/web/setup-google-pay)** — the `SetupApplePayError` / `SetupGooglePayError` unions are `"configurationError" | "genericError"`; quote problems and wallet availability surface through `onEvent` instead. The Apple Pay page also gained the previously undocumented `challenge` event. * **[`getPaymentMethods()`](/platform/sdk-reference/web/get-payment-methods)** — the response is `{ data: { paymentMethodConfigs, paymentMethods } }`; the previously documented `storedCards` field doesn't exist on the wire. * **Package name and `createClient()` usage** — remaining `@moonpay/platform` imports and `Result`-wrapped `createClient()` examples in the guides were updated to `@moonpay/platform-sdk-web` and the synchronous `Client` return. *** **Check and Connect frame references corrected** — resolved discrepancies in the [Check](/platform/frames/check) and [Connect](/platform/frames/connect) frame references to ensure the correct event payloads are documented, and removed the retired `connection_required` / `connection_pending` / `connection_unavailable` error codes from the SDK references. **Quotes now reject assets that aren't available in the active mode** — [`POST /platform/v1/quotes/buy`](/api-reference/platform/endpoints/quotes/get) returns a `400 invalid_request` error when the destination asset doesn't support the mode of your session (test or live). Previously the endpoint returned a quote even when the asset couldn't be purchased in that mode. The error includes a field-level detail identifying the asset: ```json theme={null} { "code": "invalid_request", "message": "Invalid request", "errors": [ { "field": "destination.asset.code", "message": "Destination asset is not supported in test mode." } ] } ``` In test mode, request quotes for assets on a [supported testnet](/platform/overview/test-mode#test-assets), such as `ETH` or `SOL`. **Platform SDK reference — Web** — the [Web SDK](/platform/sdk-reference/web/overview) (`@moonpay/platform-sdk-web`) is now fully documented and wired into the Platform sidebar: * **Customer connection** — [`getConnection`](/platform/sdk-reference/web/get-connection), [`connect`](/platform/sdk-reference/web/connect), [`resetConnection`](/platform/sdk-reference/web/reset-connection). * **Payment methods** — [`getPaymentMethods`](/platform/sdk-reference/web/get-payment-methods), [`deletePaymentMethod`](/platform/sdk-reference/web/delete-payment-method). * **Quotes** — [`getQuote`](/platform/sdk-reference/web/get-quote). * **Frame setup** — [`setupWidget`](/platform/sdk-reference/web/setup-widget), [`setupBuyButton`](/platform/sdk-reference/web/setup-buy-button), [`setupBuy`](/platform/sdk-reference/web/setup-buy), [`setupAddCard`](/platform/sdk-reference/web/setup-add-card), [`setupApplePay`](/platform/sdk-reference/web/setup-apple-pay), [`setupGooglePay`](/platform/sdk-reference/web/setup-google-pay), [`setupChallenge`](/platform/sdk-reference/web/setup-challenge). * **Transactions** — [`getTransaction`](/platform/sdk-reference/web/get-transaction), [`listTransactions`](/platform/sdk-reference/web/list-transactions). Web SDK pages were rewritten against the current SDK source: the package name was corrected across samples (`@moonpay/platform` → `@moonpay/platform-sdk-web`), [`createClient()`](/platform/sdk-reference/web/create-client) is now correctly typed as returning a `Client` directly, API error tables use the real `DevPlatformApiError` shape (`code` + `message` + optional `errors[]`), success envelopes use `{ data }` and `{ data, pageInfo }`, and event and error unions for every `setup*` method match what the SDK actually emits (including the `kind: "frame"` discriminator on `challenge` events, `oneTapApplePaySecondFactorRequired` for Apple Pay, and `"unsupported"` rather than an error when Google Pay is unavailable). [`getConnection`](/platform/sdk-reference/web/get-connection) documents the full set of connection statuses (including `termsAcceptanceRequired`) and `CustomerCapabilities`. *** **Challenge frame `error` event guidance corrected** — the [Handle challenges](/platform/guides/handling-challenges) guide previously told partners to surface the `message` payload from the Challenge frame's `error` event. That contradicts the [Challenge frame](/platform/frames/challenge) reference, which marks `code` and `message` as developer-facing and explicitly not for end-user UI. Updated the guide to recommend logging the `code` and `message` and showing the customer a generic next step (such as retry or choose a different payment method) instead. *** **Buy quotes now return payment disclosure IDs** — the [quote response](/api-reference/platform/endpoints/quotes/get#response-data-payment-disclosures) includes a `paymentDisclosures` array of `{ id, version }` objects that identifies exactly which disclosure(s) to render for a transaction. This replaces the need to infer disclosure requirements from `capabilities.ramps.requirements.paymentDisclosures` at connect time. The `capabilities.ramps.requirements.paymentDisclosures` property is deprecated and will be removed. *** **Apple Pay frame failure codes documented** — the [Apple Pay frame](/platform/frames/apple-pay#failure-codes) reference now lists the `failureCode` values a failed `complete` event can return (`applePayMerchantUnavailable`, `transactionNotAllowed`, `validationError`, `serviceUnavailable`, `authorizationDeclined`, and `unknown`), each with its default `failureReason` and recommended handling. The [Pay with Apple Pay](/platform/guides/pay-with-apple-pay) guide shows how to branch on `failureCode` to drive your error handling. *** **Google Pay frame failure codes documented** — the [Google Pay frame](/platform/frames/google-pay#failure-codes) reference now lists the `failureCode` values a failed `complete` event can return (`transactionNotAllowed`, `validationError`, `serviceUnavailable`, `authorizationDeclined`, and `unknown`), each with its default `failureReason` and recommended handling. The [Pay with Google Pay](/platform/guides/pay-with-google-pay) guide shows how to branch on `failureCode` to drive your error handling. **Test mode documents challenge triggers** — the [Test mode](/platform/overview/test-mode#triggering-challenges) page now explains how to force a [challenge](/platform/guides/handling-challenges) in test mode by setting the buy amount to a specific value: `48` triggers a wallet-ownership challenge (Apple Pay and card) and `49` triggers a CVV re-entry challenge (card). These triggers only apply in test mode. **Challenges and quotes docs corrected** — refreshed the guides and SDK reference to match how the SDK and API actually behave: * [Handle challenges](/platform/guides/handling-challenges) is now a flow-agnostic guide. Challenges are surfaced by frames — today the Apple Pay, Google Pay, and buy (Pay with card) frames each emit a `challenge` event whose URL is loaded into the dedicated [Challenge frame](/platform/frames/challenge). The old example showing `challenge` nested inside a quote response was incorrect and has been removed. * [Core concepts → Quotes](/platform/overview/core-concepts#quotes) no longer describes "price quotes" and "executable quotes" as two types. There is a single quote with an `executable` boolean — see the [quotes API reference](/api-reference/platform/endpoints/quotes/get) for the request fields required to receive `executable: true`. * The per-flow guides ([Apple Pay](/platform/guides/pay-with-apple-pay), [Google Pay](/platform/guides/pay-with-google-pay)) and the [`getQuote`](/platform/sdk-reference/web/get-quote) / setup-method SDK reference pages now use this single-quote framing. **Challenge frame docs cover the identity flow** — updated the [Challenge frame](/platform/frames/challenge) reference so the `complete`, `cancelled`, and `error` payloads describe both upstream flows (buy and identity): * `complete` and `cancelled` payloads are now documented as discriminated unions over `flow: "buy" | "identity"`. The `identity` variant carries `identityId` on `complete` and no extra fields on `cancelled`. * `error` codes refreshed to match the frame's wire format (`invalid_token`, `unsupported_flow`, `invalid_challenge`) and the payload type loosened to `code: string` to reflect that additional codes can be propagated from the upstream flow. **Frame theming** — every frame now accepts a `brandColor` URL parameter that seeds a runtime-derived palette across the entire UI, plus an optional `customTheme` for border radius, color scheme lock, and granular palette control. See the new [theming guide](/platform/frames/theming) for accepted formats and examples per frame. **Challenge events for Apple Pay and Google Pay** — the [Apple Pay frame](/platform/frames/apple-pay) and [Google Pay frame](/platform/frames/google-pay) now emit a `challenge` event when verification is required before a transaction can proceed. Challenge handling added to the [Apple Pay SDK reference](/platform/sdk-reference/web/setup-apple-pay), [Google Pay SDK reference](/platform/sdk-reference/web/setup-google-pay), and manual integration guides for [web](/platform/guides/manual-integration/web), [iOS](/platform/guides/manual-integration/ios), [React Native](/platform/guides/manual-integration/react-native), and [Flutter](/platform/guides/manual-integration/flutter). *** **TransactionStatus fix** — corrected the `TransactionStatus` enum value from `complete` to `completed` in the [Apple Pay frame](/platform/frames/apple-pay) and [Google Pay frame](/platform/frames/google-pay) references. **Google Pay** — new [Pay with Google Pay](/platform/guides/pay-with-google-pay) guide, [Google Pay frame](/platform/frames/google-pay) reference, and [`setupGooglePay`](/platform/sdk-reference/web/setup-google-pay) SDK method. Covers the standalone Google Pay frame integration and manual integration for [web](/platform/guides/manual-integration/web), [Android](/platform/guides/manual-integration/android), [React Native](/platform/guides/manual-integration/react-native), and [Flutter](/platform/guides/manual-integration/flutter). **EEA disclosures** — published the exact verbiage required above the Apple Pay frame and the card Pay button for customers located in the EEA, with separate text for standard crypto-assets and non-MiCA-compliant stablecoins (USDT, cUSD, DAI, PYUSD). See [Going Live](/platform/overview/going-live#disclosures-eea). **API key header** — server-to-server requests now authenticate with the `X-Api-Key` header. See [Using the Platform API](/api-reference/platform/documentation/using-the-api#server-side-authentication) and [API and SDK credentials](/platform/guides/api-and-sdk-credentials#secret-key). **Payment-disclosure geography** — `paymentDisclosures` now identifies the customer's geography. `country` is the ISO 3166-1 alpha-3 code, `administrativeArea` is included for US state-level disclosures, and `area` can identify broader regions such as `"EEA"`. **Going Live** — documented the acceptance criteria for the global rollout (except UK). Each requirement is now tagged with a geo, and new sections cover Cards, Identity API, and EEA Apple Pay disclosures (verbiage pending). See [Going Live](/platform/overview/going-live). **Preview removed** — the Developer Platform is now generally available. The "currently in preview" notice has been removed from all docs pages. *** **Card payments** — new [Pay with card](/platform/guides/pay-with-card) guide and frame references for [Add Card](/platform/frames/add-card), [Buy](/platform/frames/buy), and [Challenge](/platform/frames/challenge). Covers the full integration: listing and managing stored cards, getting a card quote, executing transactions via the headless buy frame, and handling verification challenges (SCA, 3DS, CVC re-entry, KYC). Also adds the [Delete payment method](/api-reference/platform/endpoints/payment-methods/delete) API endpoint. **Reset frame** — new headless frame at `/platform/v1/reset` that lets you log a customer out by clearing their authentication state on MoonPay's domain. Reports completion via postMessage. See [Reset](/platform/frames/reset). *** **Unified docs site** — Platform and Widget docs now live under a single Mintlify site with separate top-level tabs. Legacy `/overview/*`, `/guides/*`, `/frames/*`, `/sdk-reference/*`, and `/api-reference/*` URLs redirect to their new `/platform/*` paths. **Manual integration fixes** — corrected the WebView samples to post the payload as a string, base64-decode credentials, and handle JS dialogs. Affects [web](/platform/guides/manual-integration/web), [iOS](/platform/guides/manual-integration/ios), [Android](/platform/guides/manual-integration/android), [React Native](/platform/guides/manual-integration/react-native), and [Flutter](/platform/guides/manual-integration/flutter) guides. *** **Fee language** updated across guides to clarify partner vs. ecosystem fees. **Revoke session** — `DELETE /platform/v1/sessions` invalidates an active session token. See [Revoke a session](/api-reference/platform/endpoints/sessions/revoke). **Sessions endpoint renamed** — `POST /platform/v1/session` is now `POST /platform/v1/sessions` (plural). The old path continues to work; new integrations should use the plural form. **Apple Pay going-live guide** — added production-readiness details for the Apple Pay frame, including merchant verification and domain registration steps. See [Pay with Apple Pay](/platform/guides/pay-with-apple-pay). **`customerId` in connect/check payload** — the `complete` postMessage event now documents `customer.id`, and the session-create request documents the `customerId` field for returning users (skip the connect flow when you already have one). **Manual integration credentials** — the manual integration guides now use `clientToken` (not `sessionToken`) to initialize frames, matching the early-credential-issuance flow. **US payment-disclosure rails narrowed** — the `paymentDisclosures` capability is now documented as scoped to NY and WA within the US. Customers in other US states will not receive a `paymentDisclosures` requirement. *** **HKDF examples** — manual integration code samples consistently pass `undefined` for the `info` parameter of `hkdf()`. **Acceptance criteria page** — new compliance checklist outlining the requirements for going live. See [Going live](/platform/overview/going-live). *** **Manual integration moved** — the per-platform manual integration pages now live under [Guides → Manual Integration](/platform/guides/manual-integration/overview). *** **Using agents** — new page covering MCP client setup for Claude Code and Codex, so you can wire your agent to MoonPay's developer docs. See [Using agents](/platform/overview/using-agents). *** **Manual integration docs** — initial documented integrations for web, iOS, Android, React Native, and Flutter (graduated from hidden drafts). **Early credential issuance** — the credentials-flow guide now reflects that `clientToken` and `accessToken` are issued before authentication completes, so partners can initialize sensitive frames sooner. *** **Connect-flow low-friction callout** — added guidance to the connect-flow guide about minimizing handoffs back to MoonPay-hosted UI. *** **Apple Pay frame height** — corrected the documented frame height for the Apple Pay frame. Quote API: `fees.partner` field renamed to `fees.ecosystem`. Apple Pay: documented test-mode and frame sandbox requirements, plus a corrected frame size. Customer capabilities and payment-disclosure requirements expanded. New widget-fallback frame and integration guide. Frames protocol: documented `version: 2` and added the versioning section. Frame URLs migrated from `/v2/*` to `/platform/*` across all docs. OpenAPI now served live from `https://api.moonpay.com/platform/openapi.json` rather than checked into the repo. Removed historical `pk_test` / `pk_live` references in favor of the new credential model. Initial Apple Pay frame size and frame sandbox requirements published. Mintlify upgrade and a content-style-guide pass across guides and frames docs. # Add Card Source: https://dev.moonpay.com/platform/frames/add-card Details on working with the Add Card frame used in the [Pay with card](/platform/guides/pay-with-card) flow. ## URL ``` https://platform.moonpay.com/platform/v1/add-card ``` ## Requirements ### Size Render the frame in a modal or sheet. Width and height are flexible — size the container to fit your UI. ## Initialization parameters | Property | Type | Required | Description | | ------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `clientToken` | `string` | ✅ | The [client token](/platform/guides/api-and-sdk-credentials#client-token) returned from the [connect flow](/platform/guides/connect-a-customer). | | `channelId` | `string` | ✅ | A unique identifier for the frame generated on your client. This value is attached to each `postMessage` payload to help identify messages.

The format of this string is up to you. | | `theme` | `string` | | Pass `dark` or `light` to force a specific appearance. If you omit this, the frame uses the user's system appearance. | ## Events All events are dispatched using the message pattern described in the [frames protocol](/platform/frames/overview#frames-protocol#messages). Below are the event payloads specific to the Add Card 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. ```json Example theme={null} { "version": 2, "meta": { "channelId": "ch_1" }, "kind": "handshake" } ``` ```ts twoslash TypeScript definition theme={null} type Message = T & { version: 2; meta: { channelId: string }; }; type HandshakeEvent = Message<{ kind: "handshake"; }>; ``` #### `ready` The frame finished loading and the card input UI is fully rendered. ```json Example theme={null} { "version": 2, "meta": { "channelId": "ch_1" }, "kind": "ready" } ``` ```ts twoslash TypeScript definition theme={null} type Message = T & { version: 2; meta: { channelId: string }; }; type ReadyEvent = Message<{ kind: "ready"; }>; ``` #### `complete` The card was added successfully. Use `card.id` to get a quote without re-fetching payment methods. ```json Example theme={null} { "version": 2, "meta": { "channelId": "ch_1" }, "kind": "complete", "payload": { "card": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "type": "card", "cardType": "credit", "brand": "visa", "last4": "4242", "expirationMonth": "12", "expirationYear": "2027", "availability": { "active": true } } } } ``` ```ts twoslash TypeScript definition theme={null} type Message = T & { version: 2; meta: { channelId: string }; }; type CardResponse = { id: string; type: "card"; cardType: "credit" | "debit" | "unknown"; brand: "visa" | "mastercard" | "maestro" | "american_express" | "other"; last4: string; expirationMonth: string; expirationYear: string; availability: { active: boolean }; }; type AddCardCompleteEvent = Message<{ kind: "complete"; payload: { card: CardResponse; }; }>; ``` #### `error` An error occurred during card addition. ```json Example theme={null} { "version": 2, "meta": { "channelId": "ch_1" }, "kind": "error", "payload": { "code": "generic", "message": "Card creation failed." } } ``` ```ts twoslash TypeScript definition theme={null} type Message = T & { version: 2; meta: { channelId: string }; }; type AddCardErrorEvent = Message<{ kind: "error"; payload: { code: "configurationError" | "generic"; /** A developer-facing error message. Not intended to be rendered in UI. */ message: string; }; }>; ``` | Code | Description | | -------------------- | -------------------------------- | | `configurationError` | Missing or invalid `clientToken` | | `generic` | Card creation failed | ### Inbound events parent->frame These events are sent from the parent window to this frame. #### `ack` Acknowledge the [handshake](#handshake). ```json Example theme={null} { "version": 2, "meta": { "channelId": "ch_1" }, "kind": "ack" } ``` ```ts twoslash TypeScript definition theme={null} type Message = T & { version: 2; meta: { channelId: string }; }; type AckEvent = Message<{ kind: "ack"; }>; ``` # Apple Pay Source: https://dev.moonpay.com/platform/frames/apple-pay Details on working with the [Apple Pay](/platform/guides/pay-with-apple-pay) frame, including [guest checkout](/platform/guides/guest-checkout). ## URL ```html theme={null} https://platform.moonpay.com/platform/v1/apple-pay ``` ## Requirements ### Size The frame container **height must be 44px**. Width is flexible; the Apple Pay button inside the frame uses 100% of the container width. ### Permissions The `payment` [permission policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy#iframes) is required. In [test mode](/platform/overview/test-mode#apple-pay), the frame uses `window.confirm` to simulate the Apple Pay payment. If your iframe uses the [`sandbox`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe#sandbox) attribute, you will need to include `allow-modals`. ```tsx Example theme={null}