Skip to main content
Set up a swap channel: a persistent deposit address that accepts one stablecoin and delivers another, on the same chain or across chains. Examples:
  • A channel that turns incoming USDC on Solana into EURC on Base
  • A channel that turns incoming USDT on Ethereum into USDC on Arbitrum
This lets applications add bridging and automatic conversion without users stepping through multi-step UIs. Any wallet or frontend can integrate it with one API call.

How it works

1
The user gets a deposit address on Solana owned by MoonPay Enterprise
2
An autoramp ties that address to the destination:i. it turns all incoming USDC into EURC on Baseii. and delivers it to the user’s connected wallet
3
The user sends 1000 USDC to the deposit address
4
As soon as funds arrive, MoonPay Enterprise converts them to EURC on Base and delivers them to the destination wallet

Two ways to run a channel

Most swap channels use a standing autoramp, because the user decides how much to send. Both are shown below.

Prerequisites

Every step must complete before moving to the next. Sandbox-only steps are marked. Set these in your shell first. Every command on this page reuses them.
1

Customer is Active

Your customer must have Active status. This means they have signed the terms and conditions, completed identification (KYC/KYB), and signed any remaining required documents. See Onramp for a full onboarding walkthrough.
2

Register the recipient wallet address

Register the destination wallet via Crypto Addresses for Travel Rule compliance. Self-hosted wallets require a signed proof-of-ownership message; hosted wallets require the custodian’s DID.The wallet you pass in recipient_account must match an address you have already registered for this customer.
3

Create the channel

Either create a standing autoramp, or request an exact-out quote and create a quote-source autoramp. Both are shown below.
4

Sandbox: approve the autoramp

An autoramp is created in Authorized status. In Sandbox you advance it yourself, which also provisions the mock deposit account that a simulated deposit needs:
In production, an autoramp reaches Approved on its own once its deposit account is provisioned and verified.

Automation route: standing autoramp

Create an autoramp that converts USDC on Solana to EURC on Base at the current mid-market rate.

Request

Capturing the ID into $AUTORAMP_ID lets you paste the remaining commands on this page straight into the same shell.
POST /api/autoramps requires an Idempotency-Key header. Use a unique UUID per request to prevent duplicate autoramps.
The request uses chain in recipient_account, but the response returns the same value as blockchain in recipient. Both are correct: map between them when comparing the request and response.

Response

Error response

Validation failures return the error as a plain string body, not a structured object. A recipient_account that does not match a wallet address you’ve already registered via Crypto Addresses returns 400:
A 422 covers the cases where the request is well formed but the customer cannot complete the action yet, for example "Customer is unable to complete the action. Consult the customer abilities API", which points you at GET /api/customers/{id}/abilities (see Onboarding).
deposit_rails is empty at Authorized. Poll GET /api/autoramps/{id} or subscribe to webhooks until status = Approved before sharing deposit details with end users. See Autoramp Status.
Once the autoramp reaches Approved, the response includes the deposit wallet address under deposit_rails. Share it with the user along with the supported assets for that wallet (e.g. EURC, USDC). Non-supported assets sent to the wallet are returned to sender.
Shared EVM deposit address. An autoramp gets one deposit address per source chain, and every EVM chain on the same autoramp shares one address, so the EVM entries in deposit_rails repeat the same string. Only the token and chain combinations listed in source_currencies are converted: a deposit arriving on an EVM chain you did not declare is not swept, even though the address matches. Declare every chain you want to accept when you create the autoramp. Non-EVM chains such as Solana get their own distinct address.

Locked rate: exact-out quote

Use this when you need a precise amount of the destination token. Stablecoin to stablecoin pairs lock for up to 10 minutes.
1

Request the quote

GET /api/autoramps/quoteSet amount_out for the exact destination amount, or amount_in to price a known source amount. Never set both. recipient_account_id is the UUID of the registered crypto address.
The response returns amount_in (what the user must send), amount_out, an itemised fee breakdown, rate, rate_lock_valid_until, and a signature.
2

Create the autoramp from the quote

POST /api/autorampsSubmit the signed quote payload verbatim.
The quote is digitally signed. Any modification, including changing values or omitting fields, causes the request to fail.
3

Share the deposit address and amount

Show your customer the deposit_rails address and the exact amount_in figure. A deposit only matches a quote when its amount_in value and source currency match. Deposits that match no active quote, or only an expired one, are returned to sender.
Quote-source autoramps can keep accepting new quotes over time via POST /api/autoramps/{autoramp_id}/quotes. Standalone autoramps created without an initial quote cannot, and always execute at the current rate. See Quotes.

Test the full flow in Sandbox

Creating the is half the integration. Simulate a deposit to confirm your webhook handler and reconciliation logic work before you take real money.
1

Simulate an incoming deposit

POST /api/sandbox/transaction builds a transaction directly from the route, reusing its currencies, customer, and recipient.
The response carries amount_in, amount_out, currency_in, currency_out, and state. See Sandbox for the optional fee, fx_rate, and initial_state fields.
2

Drive the transaction to Completed

The simulated transaction starts in Pending. Advance it to trigger the rest of the status webhooks.
3

Check the webhooks you received

Your endpoint should have received a transaction event when the deposit landed, followed by transaction_status events as it moved through FundsReviewInProgress, ConversionInProgress, PayoutInProgress, and Completed.Read transaction_status, not the deprecated status field, for the current state. Payload shapes and the full status table are in Monitoring payments; signature verification is in Webhooks.
Once the destination token is delivered, transaction_hash contains the on-chain hash.

Simulate it in the Dashboard

You can run this same flow without writing a single API call. The Partner Dashboard covers every step, from creating the route to settling a simulated deposit.
1

Create the route

Switch the sidebar to the App view and pick your customer in the customer selector at the top. Open Operations → Routes, then click on the card and choose .
2

Authorize the route

Go to Developer → Sandbox and open the Autoramps tab. Your new route appears under Unverified Autoramps. Set its status to Authorized using the dropdown on the right.
The dropdown offers Created, Authorized, and Rejected. Pick Authorized: that provisions the mock deposit account, which is what the simulated deposit needs. Sending "Approved" to PUT /api/sandbox/autoramp/{id} provisions the same account and also moves the route to Approved, so use the API call if you want to see the Approved status your integration waits for.
3

Create the simulated deposit

Still under Developer → Sandbox, click New Transaction in the top right. On the Create New Sandbox Transaction screen:
  1. Customer: search by name, email, or ID and select the customer who owns the route.
  2. Ramp: pick it from the dropdown. This field still uses the older wording for a route. Entries are labelled by currency pair, for example .
  3. Amount: enter the deposit amount in the route’s input currency.
Click Execute Transaction. This bypasses the normal deposit flow and builds the transaction straight from the route configuration.
4

Settle the transaction

Back on the Sandbox page, open the Transactions tab. Your transaction is listed under Pending Transactions with Accept and Reject buttons. Choose Accept to complete it, or use the Change state dropdown to move it to a specific state and test how your integration reacts.
5

Check the result

Open the transaction from Operations → Transactions to see amounts, fees, and . Webhooks fire exactly as they do on the API path, so this is a good way to exercise your endpoint before you write any integration code.
Use Reset on the Sandbox page to clear all customers, wallets, fiat accounts, transactions, and autoramps and start from a clean state.

Move to production

Remove every /api/sandbox/* call from your integration. Those endpoints do not exist in production.

Stablecoins and Blockchains

Which tokens and chains are supported as sources and destinations

Quotes

Full quote reference: expiry policies, multi-currency input, attaching quotes over time

Crypto Addresses

Register self-hosted or hosted wallets for Travel Rule compliance

Autoramp Status

Every status an autoramp moves through, and what unblocks each one

Webhooks

Signature verification, payload schemas, and retry behaviour

Transaction Status

Status mapping, settlement times, and the polling API