Prerequisite: Your organization must be approved before it can quote. Swaps do not need a bank account. For onboarding your customers, see KYC Overview.
$ORGANIZATION_ID in the examples. Use your own organization’s id from GET /me, or a customer organization’s id when you swap for that customer. Register the wallets, quote and order under the same id. See Organizations in the route.
export ORGANIZATION_ID=<organization_id>
Swaps run on Stellar, Solana and Monad. The source and destination wallets must be on the same chain. To move the same token to a wallet on another chain, quote a cross-chain transfer instead. A quote between different tokens on different chains returns
400 invalid-request. Base, Polygon and Avalanche support onramps and offramps, not swaps.Swap flow
1
Discover assets
Show Details
Show Details
Either of these endpoints gives you the asset identifiers for a quote:
- GET /supported_assets requires auth. Pass
paymentAccountIdwith a wallet’s id to list the tokens on that wallet’s chain. - GET /lookup/stablebonds is public. It returns every stablebond on every chain, with pricing and supply data.
asset from the first, or the identifier from the second, as sourceAsset and destinationAsset.Swap rules:- At least one side must be a stablebond. The other side can be a stablecoin such as USDC, so
USDC → CETESandCETES → USDCboth work. - Both assets must be stablebonds or supported stablecoins. Non-stable tokens (SOL, XLM) return
400invalid-request. - Stablecoin-to-stablecoin swaps (
USDC → USDT) return400invalid-request. - The two assets must differ.
Asset identifier format differs by chain:
- Solana: Base58 mint address (for example
AvvetPGuuB5FD5m86fpw3LtDKyQoUFT1mG9WarNQLW4q) - Stellar:
CODE:ISSUER(for exampleCETES:GC3CW7...) - Monad: the raw token contract address only (for example
0xcC77c598d42f2f78Beb42C91d12B9d4041a5cE29)
USDC identifiers:
- Stellar sandbox:
USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5 - Stellar production:
USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN - Solana sandbox:
4D4r1KuS9WxbPCzzAJnWtYUJSSzDEZzrF1CbwajWGVjg - Solana production:
EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
2
Register the wallets: POST /organization/{organizationId}/payment_account
Show Details
Show Details
Register the wallet that sends the source asset. To deliver to a different wallet, register that one too. See Register payment account for the full schema.The source wallet needs
Sandbox
curl -X POST https://api.sand.etherfuse.com/organization/$ORGANIZATION_ID/payment_account \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"kind": "wallet",
"label": "Treasury wallet",
"accountDetails": {
"kind": "address",
"address": "GDUKMGUGD3V6VXTU2RLAUM7A2FABLMHCPWTMDHKP7HHJ6FCZKEY4PVWL",
"chain": "stellar"
}
}'
source ready on its chain rail, and the destination wallet needs destination ready. Read the account again with GET /organization/{organizationId}/payment_account/{id} to check.3
Create a quote: POST /organization/{organizationId}/quote
Show Details
Show Details
Quote between the two wallet accounts. Both accounts must belong to the organization in the route. Quotes expire 2 minutes after they are created (
expiresAt). The response includes rateBeforeFees, rateAfterFees and midMarketRate. See Create quote for the full schema.curl -X POST https://api.sand.etherfuse.com/organization/$ORGANIZATION_ID/quote \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "stored", "id": "<wallet_payment_account_id>" },
"destination": { "type": "stored", "id": "<wallet_payment_account_id>" },
"sourceAmount": "10",
"sourceAsset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"destinationAsset": "CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4"
}'
curl -X POST https://api.etherfuse.com/organization/$ORGANIZATION_ID/quote \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"source": { "type": "stored", "id": "<wallet_payment_account_id>" },
"destination": { "type": "stored", "id": "<wallet_payment_account_id>" },
"sourceAmount": "10",
"sourceAsset": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
"destinationAsset": "CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4"
}'
Partner fees: We apply your organization’s partner fee automatically. To set it for one quote, add
"partnerFeeBpsOverride": 100. We cap values above 500. See Partner fees.4
Create the order: POST /organization/{organizationId}/order
Show Details
Show Details
Send the As on an offramp,
quoteId. The response is 201 with the full order, and the order’s id equals the quoteId. See Create order for the full schema.Sandbox
curl -X POST https://api.sand.etherfuse.com/organization/$ORGANIZATION_ID/order \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{ "quoteId": "<quote_id>" }'
fundingInstructions.rail is the chain, and fundingInstructions.destination gives the address to send to, a memo when one is needed, and a preparedTransaction when we built one.5
Send the source asset
Show Details
Show Details
The swap does not start until the customer’s wallet sends the source asset. Fund the order before
fundingDeadline in one of two ways:- Sign the prepared transaction. When
preparedTransactionis present, signtransactionwith the customer’s wallet and submit it to the network. - Send a transfer yourself. Send the order’s
source.amountofsource.assettoaddress, withmemowhen it is present.
Prepared transactions go stale. A Solana or Stellar transaction goes stale after
refreshAfter. A Stellar or EVM transaction also goes stale when the wallet sends another transaction first. In either case, call POST /organization/{organizationId}/order/{id}/funding_instructions/refresh. The new funding instructions arrive a moment later in an orderUpdated webhook and on GET.6
Track the order: GET /organization/{organizationId}/order/{id}
Show Details
Show Details
We receive the source asset, run the swap on the chain’s venue, and deliver the result to the destination wallet. The status moves Swaps are orders, so List orders returns them with your onramps and offramps.
created → funded → completed. Read the order with Get order, or subscribe to orderUpdated with POST /webhook_subscription.Sandbox
curl -H "Authorization: <api_key>" \
https://api.sand.etherfuse.com/organization/$ORGANIZATION_ID/order/<order_id>
Show Chain-specific swap examples
Show Chain-specific swap examples
Stellar: USDC to CETES:Solana: USDC to CETES:
{
"source": { "type": "stored", "id": "<stellar_wallet_payment_account_id>" },
"destination": { "type": "stored", "id": "<stellar_wallet_payment_account_id>" },
"sourceAmount": "10",
"sourceAsset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"destinationAsset": "CETES:GC3CW7EDYRTWQ635VDIGY6S4ZUF5L6TQ7AA4MWS7LEQDBLUSZXV7UPS4"
}
{
"source": { "type": "stored", "id": "<solana_wallet_payment_account_id>" },
"destination": { "type": "stored", "id": "<solana_wallet_payment_account_id>" },
"sourceAmount": "10",
"sourceAsset": "4D4r1KuS9WxbPCzzAJnWtYUJSSzDEZzrF1CbwajWGVjg",
"destinationAsset": "AvvetPGuuB5FD5m86fpw3LtDKyQoUFT1mG9WarNQLW4q"
}
Show Common errors
Show Common errors
| Error | Cause | Fix |
|---|---|---|
400 invalid-request, no stablebond | Neither side is a stablebond (for example a stablecoin-to-stablecoin swap) | Make one side a stablebond. Use identifiers from /lookup/stablebonds |
400 invalid-request, non-stable asset | A side is a non-stable token (SOL, XLM, ETH) | Use a stablebond or a supported stablecoin on both sides |
400 invalid-request, cross-chain | The two wallets are on different chains and hold different tokens | Swap on one chain, or send the same token across chains |
400 invalid-request, same asset | sourceAsset equals destinationAsset | Pick two different assets |
400 quote-expired | The quote is older than 2 minutes, or does not exist | Create a new quote |
409 payment-account-not-ready | A wallet is not ready in the needed direction | Check the wallet’s rails |
422 quote-unavailable | We cannot quote this path right now, for example thin sandbox liquidity, or the chain has no swap venue | Retry with backoff. Swaps run only on Stellar, Solana and Monad |