Prerequisite: You need a KYC-approved wallet. Register one with POST /ramp/wallet (set
claimOwnership: true to inherit your organization’s KYB approval), or onboard a customer wallet via Customer Onboarding (KYC). Swaps do not require a bank account.
For onboarding your customers, see Onboarding Customers.Swaps are currently available on Stellar, Solana, and Monad, and the source and destination must be on the same chain (no cross-chain swaps). The other EVM chains (Base, Polygon) support direct on/off-ramps but do not yet support token-to-token swaps.
Swap Flow
1
Discover Assets
Show Details
Show Details
Either of these endpoints will give you the asset identifiers needed for quotes:
-
GET /ramp/assets — Requires auth. Returns all rampable stablecoins and stablebonds for the specified blockchain. The
currencyparameter controls sort priority (matching assets appear first). Best for building integrations where you already know the customer’s wallet. - GET /lookup/stablebonds — Public, no auth. Returns all stablebonds across all chains with pricing and supply data. Best for exploring what’s available before onboarding a customer.
identifier from either response as the asset value in your quote.Swap constraint: A swap must involve at least one stablebond; the other side can be a stablecoin such as USDC, so both USDC → CETES and the reverse CETES → USDC are valid. Both assets must be stable, so non-stable tokens (SOL, XLM) return Non-stable assets are not supported, and stablecoin-to-stablecoin swaps (USDC → USDT) are not supported.Asset identifier format differs by chain:
- Solana — Base58 mint address (e.g.
AvvetPGuuB5FD5m86fpw3LtDKyQoUFT1mG9WarNQLW4q) - Stellar —
CODE:ISSUERformat (e.g.CETES:GC3CW7...) - Monad — raw token contract address only (e.g.
0xcC77c598d42f2f78Beb42C91d12B9d4041a5cE29)
SYMBOL:0x... (Stellar format) on other chains will return UnsupportedBlockchain.USDC addresses for swap sources:
- Stellar sandbox:
USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5 - Stellar production:
USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN - Solana sandbox:
4D4r1KuS9WxbPCzzAJnWtYUJSSzDEZzrF1CbwajWGVjg - Solana production:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
2
Create a Swap Quote — POST /ramp/quote
Show Details
Show Details
Quotes expire after 2 minutes. For swaps, set
type: "swap" with the source token identifier as sourceAsset and the stablebond identifier as targetAsset. The response includes requiresSwap: true and the exchangeRate between the two assets. See POST /ramp/quote for the full schema.curl -X POST https://api.sand.etherfuse.com/ramp/quote \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"quoteId": "<uuid>", # You generate this UUID
"customerId": "<org_uuid>", # From onboarding
"blockchain": "stellar",
"quoteAssets": {
"type": "swap",
"sourceAsset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"targetAsset": "CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4"
},
"sourceAmount": "10"
}'
curl -X POST https://api.etherfuse.com/ramp/quote \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"quoteId": "<uuid>", # You generate this UUID
"customerId": "<org_uuid>", # From onboarding
"blockchain": "stellar",
"quoteAssets": {
"type": "swap",
"sourceAsset": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
"targetAsset": "CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4"
},
"sourceAmount": "10"
}'
Partner fees: If your organization has a partner fee configured, it is applied automatically. To override per-quote, add
"partnerFeeBps": 100 (0–500) to the request body. See Partner Fees for details.3
Create the Swap — POST /ramp/swap
Show Details
Show Details
Unlike orders, the swap endpoint requires
blockchain and publicKey in the request body (these are not derived from the quote). The endpoint returns an empty 200 — all swap progress is delivered via webhooks. See POST /ramp/swap for the full schema.curl -X POST https://api.sand.etherfuse.com/ramp/swap \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"orderId": "<uuid>", # You generate this UUID
"quoteId": "<quote_uuid_from_step_2>", # From Step 2
"publicKey": "<wallet_public_key>", # From onboarding
"blockchain": "stellar"
}'
curl -X POST https://api.etherfuse.com/ramp/swap \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"orderId": "<uuid>", # You generate this UUID
"quoteId": "<quote_uuid_from_step_2>", # From Step 2
"publicKey": "<wallet_public_key>", # From onboarding
"blockchain": "stellar"
}'
The
orderId here is a swap identifier you generate — it tracks this swap through webhooks. The optional targetWallet field lets you send the swapped tokens to a different wallet than publicKey.4
Sign the Transaction
Show Details
Show Details
After creating the swap, you’ll receive a Sign the
swap_updated webhook with the sendTransaction — a pre-built, encoded transaction ready to sign.{
"swap_updated": {
"orderId": "<swap_uuid>",
"customerId": "<org_uuid>",
"sendTransaction": "AQAAAA...encoded_transaction",
"status": "created"
}
}
sendTransaction using a wallet adapter and submit it to the testnet blockchain. Once confirmed, you’ll receive another swap_updated webhook with status: "completed".Do not build your own transaction. Always use the
sendTransaction provided in the webhook. This transaction is pre-built by Etherfuse with the correct swap parameters.If the transaction expires before you submit it, fetch a fresh one with POST /ramp/swap/{order_id}/regenerate_tx — the new
sendTransaction arrives over the swap_updated webhook. (Stellar transactions expire in ~1–2 minutes.) sendTransaction is only present while funds are still owed; after they’re received you instead get sendTransactionHash and receiveTransactionHash.5
Verify Completion
Show Details
Show Details
Monitor swap progress via
swap_updated webhooks. Status progression: swap_created → swap_funded → swap_completed (or swap_failed). Register a webhook for the swap_updated event type via POST /ramp/webhook.Unlike on/off-ramps, there is no polling endpoint for swap status — webhooks are the only way to track progress.
Interactive Tutorial
Use the interactive tutorial below to test the swap flow with your sandbox API key. Each step builds on the previous one, and you can edit the request bodies before sending.Show Chain-specific swap examples
Show Chain-specific swap examples
Stellar — USDC to CETES:Solana — USDC to CETES:
{
"blockchain": "stellar",
"quoteAssets": {
"type": "swap",
"sourceAsset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"targetAsset": "CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4"
}
}
{
"blockchain": "solana",
"quoteAssets": {
"type": "swap",
"sourceAsset": "4D4r1KuS9WxbPCzzAJnWtYUJSSzDEZzrF1CbwajWGVjg",
"targetAsset": "AvvetPGuuB5FD5m86fpw3LtDKyQoUFT1mG9WarNQLW4q"
}
}
Show Common errors
Show Common errors
| Error | Cause | Fix |
|---|---|---|
Swaps must involve at least one stablebond asset | Neither side is a stablebond (e.g. a stablecoin-to-stablecoin swap) | Make at least one side a stablebond — use identifiers from /lookup/stablebonds |
Non-stable assets are not supported | A side is a non-stable token (SOL, XLM, ETH) | Both sides must be a stablebond or a supported stablecoin |
UnsupportedBlockchain | Swaps not yet available on this chain, or using wrong identifier format | Swaps are currently available on Stellar, Solana, and Monad. Check your asset identifier format. |
Quote not found or expired | Quote TTL is 2 minutes | Create a fresh quote |
Terms and conditions have not been completed | Wallet not KYC-approved | Complete the onboarding flow |
Temporarily unable to get a valid quote for this trading path (424) | Sandbox liquidity for that path is thin right now, not a direction restriction (reverse stablebond → USDC swaps are valid) | Transient. Retry with backoff. See Errors. |
Unexpected | Swap provider (Jupiter/SDEX) returned an error | Check that the source asset has testnet liquidity against the target |