This API allows you to integrate crypto on/off ramps into your application. Your customers can buy crypto with fiat (onramp) or sell crypto for fiat (offramp).
Integration Flow
The typical integration follows this pattern:
- Onboard Customer
- Get Assets
- Create Quote
- Create Order
- Monitor Order via Webhooks
1. Onboard Customer
Generate a presigned URL and have your customer complete KYC verification.
POST /ramp/onboarding-url
{
"customerId": "34fcc67a-2454-4578-8435-e2bf03b4dc30",
"bankAccountId": "a1b2c3d4-5678-9abc-def0-1234567890ab",
"publicKey": "GDUKMGUGD3V6VXTU2RLAUM7A2FABLMHCPWTMDHKP7HHJ6FCZKEY4PVWL",
"blockchain": "stellar"
}
ID Consistency is CriticalThe customerId, bankAccountId, and publicKey you provide during onboarding are linked together on Etherfuse’s side. You must use these same values when creating quotes and orders for that customer:
customerId — You generate this UUID. Use the same value when creating quotes.
bankAccountId — You generate this UUID. Use the same value when creating orders.
publicKey — The customer’s wallet address. This wallet is registered with Etherfuse during onboarding. Use the same value when creating orders.
Setup checklist:
- Generate a UUID for
customerId — store it in your system
- Generate a UUID for
bankAccountId — store it in your system
- Call
POST /ramp/onboarding-url with both IDs and the customer’s publicKey
- Have the customer complete onboarding (KYC + bank account linking)
- Never change these IDs after onboarding — they are permanently bound
All three are linked during onboarding and must be used together. Each customer should have their own unique customerId and bankAccountId. You cannot mix and match IDs across different customers — for example, using a bankAccountId that was onboarded with a different customerId and publicKey will cause order creation to fail.The customer’s wallet (publicKey) is registered with Etherfuse as part of onboarding. The customer must complete the full onboarding flow (including KYC verification) before their wallet can be used for orders.
The customer must complete KYC verification before they can transact. There are two options:
- Presigned URL flow: The customer visits the returned
presigned_url to complete identity verification and link their bank account in the Etherfuse UI.
- Programmatic KYC: You collect identity data in your own UI and submit it via API, allowing you to pre-populate the KYC data on behalf of the customer. See the Testing KYC guide.
After all data is submitted, Etherfuse reviews the information for accuracy before approving the customer. You’ll receive a customer_updated webhook when verification is complete.
2. Get Available Assets
Call /ramp/assets to show your user which assets they can buy or sell.
GET /ramp/assets?blockchain=stellar¤cy=mxn&wallet=GDUKMGUGD3V6...
Response:
{
"assets": [
{
"symbol": "USDC",
"identifier": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
"name": "USD Coin",
"currency": "mxn",
"balance": "100.50",
"image": "https://example.com/usdc.png"
},
{
"symbol": "CETES",
"identifier": "CETES:GCRYUGD5NVARGXT56XEZI5CIFCQETYHAPQQTHO203IQZTHDHH4LATMYWC",
"name": "CETES",
"currency": "mxn",
"balance": "50.00",
"image": "https://example.com/cetes.png"
}
]
}
Use the identifier field when creating quotes.
Exchange RatesFor raw USD exchange rates (e.g., USD to MXN), use the public GET /lookup/exchange_rate endpoint — no authentication required. The /ramp/assets endpoint is for discovering rampable assets and their identifiers, not exchange rates.
3. Create Quote
Get pricing for the conversion. Quotes expire after 2 minutes.
Onramp (buying USDC with MXN):
Use the same customerId from onboarding:
{
"quoteId": "844393ca-be57-4817-a58a-5b60e2792c10",
"customerId": "34fcc67a-2454-4578-8435-e2bf03b4dc30",
"blockchain": "stellar",
"quoteAssets": {
"type": "onramp",
"sourceAsset": "MXN",
"targetAsset": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
},
"sourceAmount": "1000"
}
Offramp (selling USDC for MXN):
{
"quoteId": "a44393ca-be57-4817-a58a-5b60e2792c12",
"customerId": "34fcc67a-2454-4578-8435-e2bf03b4dc30",
"blockchain": "stellar",
"quoteAssets": {
"type": "offramp",
"sourceAsset": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
"targetAsset": "MXN"
},
"sourceAmount": "50"
}
Quote Response:
{
"quoteId": "844393ca-be57-4817-a58a-5b60e2792c10",
"blockchain": "stellar",
"quoteAssets": {
"type": "onramp",
"sourceAsset": "MXN",
"targetAsset": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
},
"sourceAmount": "1000",
"destinationAmount": "49.50",
"destinationAmountAfterFee": "49.40",
"feeBps": "20",
"feeAmount": "2.00",
"exchangeRate": "0.0495",
"expiresAt": "2025-12-17T18:31:46.195307961Z"
}
4. Create Order
Execute the order using the quote. The bankAccountId and publicKey must match the values used when this customer was onboarded. The order inherits the direction and amounts from the quote.
{
"orderId": "f7e8d9c0-1234-5678-9abc-def012345678",
"bankAccountId": "a1b2c3d4-5678-9abc-def0-1234567890ab",
"publicKey": "GDUKMGUGD3V6VXTU2RLAUM7A2FABLMHCPWTMDHKP7HHJ6FCZKEY4PVWL",
"quoteId": "844393ca-be57-4817-a58a-5b60e2792c10"
}
Response (Onramp):
{
"onramp": {
"orderId": "f7e8d9c0-1234-5678-9abc-def012345678",
"depositClabe": "646180157034567890",
"depositAmount": 1000.00
}
}
Response (Offramp):
{
"offramp": {
"orderId": "f7e8d9c0-1234-5678-9abc-def012345678"
}
}
For onramps, the customer deposits MXN to the depositClabe. For offramps, see the Offramp Signing section below.
5. Monitor Order via Webhooks
After creating an order, monitor its progress via webhooks.
Onramp:
- The customer must send MXN to the
depositClabe returned in the create order response
- Once we confirm receipt of the fiat, you receive
order_funded
- We mint/transfer crypto and send it to the customer’s wallet
- You receive
order_completed with confirmedTxSignature containing the blockchain transaction hash
Offramp:
- You receive
order_created with burnTransaction containing a partially signed, encoded transaction
- Have the customer sign the
burnTransaction using a wallet adapter and submit it to the blockchain (see Offramp Signing)
- Once confirmed on-chain, you receive
order_funded
- We send MXN to the customer’s bank account
- You receive
order_completed
Swap (crypto to crypto):
- Create a quote with
type: "swap" specifying source and target crypto assets
- Call
/ramp/swap with the quoteId to initiate the swap
- You receive
swap_updated webhook with transaction to sign
- Have the customer sign and submit the transaction
- You receive
swap_updated when completed
If a transaction expires before submission, use /ramp/order/{order_id}/regenerate_tx or /ramp/swap/{order_id}/regenerate_tx to get a new transaction.
See the Webhooks section below for detailed payload examples.
Use webhooks for order tracking. Webhooks deliver order updates as soon as they happen. If you poll the list orders endpoint instead, be aware there may be a brief indexing delay before newly created orders appear. We recommend using webhooks as the primary mechanism for tracking order progress, and storing the orderId from the create order response for immediate reference.
Offramp Signing
When an offramp order is created, the order_updated webhook includes a burnTransaction field containing a partially signed, encoded transaction. To complete the offramp:
- Take the
burnTransaction from the webhook payload
- Sign it using a wallet adapter (e.g., Stellar SDK, Solana wallet adapter) with the wallet specified during onboarding
- Submit the signed transaction to the blockchain
Do not build your own transaction. Always use the burnTransaction provided in the webhook. This transaction is pre-built by Etherfuse with the correct parameters.
Alternative: Hosted signing page
If you don’t want to handle transaction signing in your application, the webhook payload includes a statusPage URL (e.g., https://status.etherfuse.com/order/{orderId}). You can redirect the customer to this Etherfuse-branded page where they can review and sign the transaction directly.
Fees
Fees for onramps and offramps are based on your rolling 30-day transaction volume. Higher volume means lower fees.
| 30-Day Volume (USD) | Fee |
|---|
| 0 - 5 million | 20 bps (0.20%) |
| 5 - 10 million | 15 bps (0.15%) |
| 10 - 50 million | 10 bps (0.10%) |
| 50 - 100 million | 8 bps (0.08%) |
| 100 million+ | 5 bps (0.05%) |
The fee is calculated on the output amount and included in the quote response (feeBps, feeAmount, destinationAmountAfterFee). Your current fee tier is applied automatically when you create a quote.
Quote Types
| Type | Source | Target | Use Case |
|---|
onramp | Fiat (MXN) | Crypto (USDC, CETES) | Customer buys crypto |
offramp | Crypto (USDC, CETES) | Fiat (MXN) | Customer sells crypto |
swap | Crypto | Crypto | Customer exchanges one crypto for another |
Webhooks
Subscribe to webhooks to receive real-time updates. Webhook payloads are signed with HMAC-SHA256 using your webhook secret. The signature is in the X-Signature header as sha256={hex_signature}.
Event Types
| Event Type | Description |
|---|
customer_updated | KYC verification status changed |
bank_account_updated | Bank account verification status changed |
order_updated | Order status changed |
swap_updated | Swap transaction status changed |
kyc_updated | KYC submission status changed (for programmatic KYC) |
Webhook Status Values
Each webhook includes a status field indicating what triggered the event:
Order statuses (order_updated):
order_created - Order has been created
order_funded - Order has been funded (fiat received for onramp, crypto confirmed for offramp)
order_completed - Order completed successfully
order_failed - Order failed
order_refunded - Order was refunded (onramp only)
order_canceled - Order was canceled
Customer statuses (customer_updated):
customer_pending - Customer verification in progress
customer_verified - Customer successfully verified
customer_failed - Customer verification failed
Bank account statuses (bank_account_updated):
bank_account_pending - Bank account verification pending
bank_account_awaiting_deposit_verification - Waiting for deposit verification
bank_account_active - Bank account is active and ready to use
bank_account_inactive - Bank account is inactive
Swap statuses (swap_updated):
swap_created - Swap has been created
swap_completed - Swap completed successfully
swap_failed - Swap failed
KYC statuses (kyc_updated):
kyc_proposed - KYC data submitted, awaiting admin review
kyc_approved - KYC approved (on-chain marking will follow for Solana)
kyc_rejected - KYC rejected (includes updateReason with rejection details)
Order Status Flow
Orders progress through different statuses. You’ll receive an order_updated webhook at each transition.
Onramp Order Flow (customer buys crypto with MXN):
created → funded → completed
↓ ↓
failed refunded
↓
canceled
created - Order created, waiting for customer to deposit MXN to the depositClabe
funded - MXN deposit received and confirmed
completed - Crypto sent to customer’s wallet (confirmedTxSignature contains the tx hash)
Offramp Order Flow (customer sells crypto for MXN):
created → funded → completed
↓ ↓
failed failed
↓
canceled
created - Order created, burnTransaction contains the transaction for the customer to sign
funded - Customer’s crypto transaction confirmed on-chain
completed - MXN sent to customer’s bank account
Swap Flow (customer exchanges crypto for crypto):
created → funded → completed
↓ ↓
failed failed
created - Swap created, transaction ready for customer to sign
funded - Customer’s crypto transaction confirmed on-chain
completed - Target crypto sent to customer’s wallet
Alternative Statuses:
failed - Order/swap failed (timeout, error, etc.)
refunded - Funds returned to customer (onramp only)
canceled - Order canceled before funding
Webhook Payload Examples
Order Created (Onramp):
{
"order_updated": {
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"customerId": "123e4567-e89b-12d3-a456-426614174001",
"walletId": "223e4567-e89b-12d3-a456-426614174002",
"bankAccountId": "323e4567-e89b-12d3-a456-426614174003",
"orderType": "onramp",
"status": "created",
"amountInFiat": 1000.00,
"depositClabe": "646180157034567890",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:00:00Z",
"statusPage": "https://status.etherfuse.com/order/123e4567-e89b-12d3-a456-426614174000"
}
}
Order Funded (Onramp):
{
"order_updated": {
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"customerId": "123e4567-e89b-12d3-a456-426614174001",
"walletId": "223e4567-e89b-12d3-a456-426614174002",
"bankAccountId": "323e4567-e89b-12d3-a456-426614174003",
"orderType": "onramp",
"status": "funded",
"amountInFiat": 1000.00,
"amountInTokens": 49.50,
"depositClabe": "646180157034567890",
"feeBps": 20,
"feeAmountInFiat": 2.00,
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:05:00Z",
"statusPage": "https://status.etherfuse.com/order/123e4567-e89b-12d3-a456-426614174000"
}
}
Order Completed (Onramp):
{
"order_updated": {
"orderId": "123e4567-e89b-12d3-a456-426614174000",
"customerId": "123e4567-e89b-12d3-a456-426614174001",
"walletId": "223e4567-e89b-12d3-a456-426614174002",
"bankAccountId": "323e4567-e89b-12d3-a456-426614174003",
"orderType": "onramp",
"status": "completed",
"amountInFiat": 1000.00,
"amountInTokens": 49.50,
"confirmedTxSignature": "3Kz8V2xYm9...abc123",
"depositClabe": "646180157034567890",
"feeBps": 20,
"feeAmountInFiat": 2.00,
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:10:00Z",
"completedAt": "2025-01-15T10:10:00Z",
"statusPage": "https://status.etherfuse.com/order/123e4567-e89b-12d3-a456-426614174000"
}
}
Order Created (Offramp):
{
"order_updated": {
"orderId": "423e4567-e89b-12d3-a456-426614174000",
"customerId": "123e4567-e89b-12d3-a456-426614174001",
"walletId": "223e4567-e89b-12d3-a456-426614174002",
"bankAccountId": "323e4567-e89b-12d3-a456-426614174003",
"orderType": "offramp",
"status": "created",
"amountInTokens": 50.00,
"burnTransaction": "AQAAAA...encoded_transaction_for_user_to_sign",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:00:00Z",
"statusPage": "https://status.etherfuse.com/order/423e4567-e89b-12d3-a456-426614174000"
}
}
Order Completed (Offramp):
{
"order_updated": {
"orderId": "423e4567-e89b-12d3-a456-426614174000",
"customerId": "123e4567-e89b-12d3-a456-426614174001",
"walletId": "223e4567-e89b-12d3-a456-426614174002",
"bankAccountId": "323e4567-e89b-12d3-a456-426614174003",
"orderType": "offramp",
"status": "completed",
"amountInTokens": 50.00,
"amountInFiat": 1000.00,
"confirmedTxSignature": "5Mz9X3yAn1...ghi789",
"feeBps": 20,
"feeAmountInFiat": 2.00,
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:15:00Z",
"completedAt": "2025-01-15T10:15:00Z",
"statusPage": "https://status.etherfuse.com/order/423e4567-e89b-12d3-a456-426614174000"
}
}
Swap Created:
{
"swap_updated": {
"orderId": "523e4567-e89b-12d3-a456-426614174000",
"customerId": "123e4567-e89b-12d3-a456-426614174001",
"sendTransaction": "AQAAAA...encoded_transaction_for_user_to_sign",
"sendTransactionHash": "",
"status": "created",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:00:00Z"
}
}
Swap Completed:
{
"swap_updated": {
"orderId": "523e4567-e89b-12d3-a456-426614174000",
"customerId": "123e4567-e89b-12d3-a456-426614174001",
"sendTransaction": "AQAAAA...encoded_tx",
"sendTransactionHash": "4Lz9W3yZn0...def456",
"receiveTransactionHash": "5Mz0X4zAo1...ghi789",
"status": "completed",
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:02:00Z"
}
}
KYC Approved:
{
"kyc_updated": {
"customerId": "123e4567-e89b-12d3-a456-426614174000",
"walletPublicKey": "9Qx7r...",
"approved": true,
"updateReason": "KYC approved by admin"
}
}
KYC Rejected:
{
"kyc_updated": {
"customerId": "123e4567-e89b-12d3-a456-426614174000",
"walletPublicKey": "9Qx7r...",
"approved": false,
"updateReason": "ID document is expired. Please submit a valid government-issued ID."
}
}
Resources
The API manages these resources, all scoped to your organization:
- Customers (
customerId) — Your end users who complete KYC. You generate this UUID and provide it during onboarding.
- Bank Accounts (
bankAccountId) — Mexican bank accounts linked to a specific customer. You generate this UUID during onboarding. Must be used with the same customer it was onboarded with. Compliance is derived — accounts are compliant if explicitly marked or if the owning organization is approved.
- Wallets (
publicKey) — Crypto wallet addresses associated with customers during onboarding. Can be registered programmatically via POST /ramp/wallet with optional claimed ownership. When an approved organization claims ownership of a wallet, it is treated as compliant without individual KYC.
- Orders (
orderId) — Onramp and offramp transactions. You generate this UUID. Each order references a customer’s bankAccountId and publicKey.
- Webhooks — Event notification subscriptions.
A customer, their bank account, and their wallet are all linked together during onboarding. When creating quotes and orders, you must use the same customerId, bankAccountId, and publicKey that were provided during that customer’s onboarding.
Troubleshooting
Common Errors
| Error Message | Cause | Fix |
|---|
"Proxy account not found" | The customerId or publicKey in your quote/order doesn’t match what was used during onboarding | Verify you’re using the exact same customerId and publicKey from the onboarding call |
"Bank account not found" | The bankAccountId in your order doesn’t match what was used during onboarding, or it belongs to a different customer | Verify you’re using the same bankAccountId that was linked to this customerId during onboarding |
401 Unauthorized | Invalid API key or incorrect auth header format | Ensure the header is Authorization: your-api-key with no Bearer prefix |
| Quote expired | Order was created more than 2 minutes after the quote | Create a new quote and submit the order promptly |
Common Pitfalls
- Onboarding must be completed before transacting. The customer’s wallet is registered during onboarding. Orders will fail if the customer hasn’t completed the full onboarding flow (including KYC verification).
- IDs are permanently bound after onboarding. Generate and store your
customerId and bankAccountId UUIDs before calling the onboarding endpoint. Changing them afterward will break the link.
- Offramp: use the provided
burnTransaction. Do not build your own transaction. The burnTransaction in the webhook is pre-built and partially signed by Etherfuse.
- Stellar: add trust lines before ramping. On Stellar, the customer’s wallet must have trust lines established for the assets they are ramping into (e.g., CETES, USDC) before tokens can be received. Use the
/ramp/assets endpoint to discover the correct asset identifiers (in CODE:ISSUER format) for setting up trust lines.