> ## Documentation Index
> Fetch the complete documentation index at: https://docs.etherfuse.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Test Onramps

> How to test onramp flows in the sandbox environment

<Info>
  **Prerequisite:** You need a KYC-approved wallet and an active bank account on your organization.
  See [Onboarding Customers](/guides/onboarding) for the full onboarding flow.
</Info>

## Onramp Flow

<Steps>
  <Step title="Discover Assets">
    <Expandable title="Details">
      Either of these endpoints will give you the asset identifiers needed for quotes:

      * [GET /ramp/assets](/api-reference/assets/get-rampable-assets) — Requires auth. Returns all rampable stablecoins and stablebonds for the specified blockchain. The `currency` parameter controls sort priority (matching assets appear first). Best for building integrations where you already know the customer's wallet.

      * [GET /lookup/stablebonds](/api-reference/lookup/get-all-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.

      Use the `identifier` from either response as the asset value in your quote.

      <Warning>
        **Asset identifier format differs by chain:**

        * **Solana** — Base58 mint address (e.g. `AvvetPGuuB5FD5m86fpw3LtDKyQoUFT1mG9WarNQLW4q`)
        * **Stellar** — `CODE:ISSUER` format (e.g. `CETES:GC3CW7...`)
        * **EVM (Base, Polygon)** — Raw contract address only (e.g. `0xcC77c598d42f2f78Beb42C91d12B9d4041a5cE29`)

        Using `SYMBOL:0x...` on EVM chains will return `UnsupportedBlockchain`.
      </Warning>
    </Expandable>
  </Step>

  <Step title="Create a Quote — POST /ramp/quote">
    <Expandable title="Details">
      All orders require a quote. Quotes expire after **2 minutes**. For onramps, set `type: "onramp"` with the fiat currency as `sourceAsset` and the token identifier as `targetAsset`. See [POST /ramp/quote](/api-reference/quotes/get-quote-for-conversion) for the full schema.

      <CodeGroup>
        ```bash Sandbox theme={null}
        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": "base",
            "quoteAssets": {
              "type": "onramp",
              "sourceAsset": "MXN",
              "targetAsset": "0xcC77c598d42f2f78Beb42C91d12B9d4041a5cE29"
            },
            "sourceAmount": "100"
          }'
        ```

        ```bash Production theme={null}
        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": "base",
            "quoteAssets": {
              "type": "onramp",
              "sourceAsset": "MXN",
              "targetAsset": "0x834df4C1d8f51Be24322E39e4766697BE015512F"
            },
            "sourceAmount": "100"
          }'
        ```
      </CodeGroup>

      <Tip>
        **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](/overview#partner-fees) for details.
      </Tip>
    </Expandable>
  </Step>

  <Step title="Create an Order — POST /ramp/order">
    <Expandable title="Details">
      The `quoteId` carries the direction, blockchain, and amounts from your quote — no need to repeat them. The response includes `orderId`, `depositClabe` (the CLABE the customer wires MXN to in production), `depositBankName` (e.g. "STP"), and `depositAccountHolder` (e.g. "Etherfuse MX"). See [POST /ramp/order](/api-reference/orders/create-a-new-order) for the full schema.

      <CodeGroup>
        ```bash Sandbox theme={null}
        curl -X POST https://api.sand.etherfuse.com/ramp/order \
          -H "Authorization: <api_key>" \
          -H "Content-Type: application/json" \
          -d '{
            "orderId": "<uuid>",              # You generate this UUID
            "bankAccountId": "<bank_account_uuid>", # From onboarding
            "cryptoWalletId": "<wallet_uuid>",   # From onboarding
            "quoteId": "<quote_id_from_step_1>"  # From Step 2
          }'
        ```

        ```bash Production theme={null}
        curl -X POST https://api.etherfuse.com/ramp/order \
          -H "Authorization: <api_key>" \
          -H "Content-Type: application/json" \
          -d '{
            "orderId": "<uuid>",              # You generate this UUID
            "bankAccountId": "<bank_account_uuid>", # From onboarding
            "cryptoWalletId": "<wallet_uuid>",   # From onboarding
            "quoteId": "<quote_id_from_step_1>"  # From Step 2
          }'
        ```
      </CodeGroup>

      <Warning>
        **One open order per bank account + amount.** If a customer already has an open order for the same amount on the same bank account, creating a second order will be rejected. The first order must be cancelled before a new one with the same amount and bank account can be created.
      </Warning>

      <Tip>
        The response includes a `statusPage` URL — e.g. `https://devnet.etherfuse.com/ramp/order/<order_uuid>`. Open it in a browser to track progress, sign transactions, or debug without polling the API.
      </Tip>
    </Expandable>
  </Step>

  <Step title="Simulate the Fiat Deposit — POST /ramp/order/fiat_received">
    <Expandable title="Details">
      In production, the customer sends MXN to the deposit CLABE. In sandbox, simulate the deposit using the [Simulate Fiat Received](/sandbox-api/fiat-received) endpoint with the `orderId` from the previous step.

      <Info>This endpoint is sandbox-only. In production, fiat deposits are detected automatically via STP.</Info>

      <Warning>
        **The deposit amount must match the order amount.** In production, if a customer sends an amount that does not match the open order, the funds will be returned to the sender. Make sure the customer transfers the exact amount specified in the order.
      </Warning>
    </Expandable>
  </Step>

  <Step title="Verify Completion — GET /ramp/order/{order_uuid}">
    <Expandable title="Details">
      Poll the order status or listen for webhooks. Status progression: `created` → `funded` → `completed`. See [GET /ramp/order](/api-reference/orders/get-order-details) for the full response schema.

      <CodeGroup>
        ```bash Sandbox theme={null}
        curl -H "Authorization: <api_key>" \
          https://api.sand.etherfuse.com/ramp/order/<order_uuid>
        ```

        ```bash Production theme={null}
        curl -H "Authorization: <api_key>" \
          https://api.etherfuse.com/ramp/order/<order_uuid>
        ```
      </CodeGroup>
    </Expandable>
  </Step>
</Steps>

<Expandable title="Chain-specific quote examples">
  **Solana** — Use the Base58 mint address as `targetAsset`:

  ```json theme={null}
  {
    "blockchain": "solana",
    "quoteAssets": {
      "type": "onramp",
      "sourceAsset": "MXN",
      "targetAsset": "AvvetPGuuB5FD5m86fpw3LtDKyQoUFT1mG9WarNQLW4q"
    }
  }
  ```

  **Stellar** — Use `CODE:ISSUER` format. Include `walletAddress` to get accurate fees for new wallets:

  ```json theme={null}
  {
    "blockchain": "stellar",
    "quoteAssets": {
      "type": "onramp",
      "sourceAsset": "MXN",
      "targetAsset": "CETES:GC3CW7..."
    },
    "walletAddress": "GDUKMGUGD3V6VXTU2RLAUM7A2FABLMHCPWTMDHKP7HHJ6FCZKEY4PVWL"
  }
  ```

  **Base / Polygon** — Use the raw contract address:

  ```json theme={null}
  {
    "blockchain": "base",
    "quoteAssets": {
      "type": "onramp",
      "sourceAsset": "MXN",
      "targetAsset": "0xcC77c598d42f2f78Beb42C91d12B9d4041a5cE29"
    }
  }
  ```
</Expandable>

<Expandable title="Common errors">
  | Error                                          | Cause                                                                        | Fix                                                                           |
  | ---------------------------------------------- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
  | `UnsupportedBlockchain`                        | Using `SYMBOL:0x...` format on an EVM chain                                  | Use the raw contract address only                                             |
  | `Terms and conditions have not been completed` | Wallet not KYC-approved                                                      | Complete the [onboarding flow](/guides/onboarding)                            |
  | `Quote not found or expired`                   | Quote TTL is 2 minutes                                                       | Create a fresh quote                                                          |
  | `Bank account not found`                       | Bank account not owned by the requesting org                                 | Verify the bank account belongs to your organization                          |
  | `quote_id is required`                         | Missing quote — the quoteless path is deprecated                             | Create a quote via `POST /ramp/quote` first                                   |
  | Duplicate order rejected                       | An open order already exists for the same amount on the same bank account    | Cancel the existing order first, or use a different amount/bank account       |
  | Deposit returned                               | Customer sent an amount that doesn't match the open order                    | Ensure the customer sends the exact amount from the order                     |
  | `Order too small`                              | Source amount too small to cover the onboarding fee for a new Stellar wallet | Increase the order amount — the onboarding fee cannot exceed 50% of the order |
  | `Invalid wallet address`                       | `walletAddress` is not a valid Stellar public key                            | Ensure the address is a valid Stellar G... public key                         |
</Expandable>

***

## Stellar: First-Time Wallet Onramp

When onramping to a Stellar wallet for the first time, the wallet may not have an on-chain account or the required trustline for the target token. Etherfuse handles this automatically using **claimable balances** — no pre-setup required from the user.

### How It Works

1. **Pass `walletAddress` in the quote request.** The system checks whether the wallet exists on-chain and has a trustline. If setup is needed, the quote fee (`feeBps` / `feeAmount`) will include a one-time onboarding cost to cover the XLM needed for account creation and/or trustline reserve.

2. **Create the order as usual.** The wallet used for the order must match the `walletAddress` from the quote.

3. **After the order completes**, the order response will include two new fields:
   * `stellarClaimableBalanceId` — The Stellar claimable balance ID (hex)
   * `stellarClaimTransaction` — An unsigned Stellar transaction XDR (base64) containing `ChangeTrust` and `ClaimClaimableBalance` operations

4. **The user signs and submits the claim transaction.** This single transaction adds the trustline and claims the tokens in one step. The user signs `stellarClaimTransaction` with their Stellar wallet and submits it to Horizon.

<Info>
  If the wallet already has an account and trustline, the flow is identical to a normal onramp — no claimable balance is used, and `stellarClaimableBalanceId` / `stellarClaimTransaction` will not be present on the order.
</Info>

### Example: Quote with Wallet Address

```bash theme={null}
curl -X POST https://api.sand.etherfuse.com/ramp/quote \
  -H "Authorization: <api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteId": "<uuid>",
    "customerId": "<org_uuid>",
    "blockchain": "stellar",
    "quoteAssets": {
      "type": "onramp",
      "sourceAsset": "MXN",
      "targetAsset": "USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"
    },
    "sourceAmount": "1000",
    "walletAddress": "GDUKMGUGD3V6VXTU2RLAUM7A2FABLMHCPWTMDHKP7HHJ6FCZKEY4PVWL"
  }'
```

If the wallet needs setup, the response `feeBps` will be higher than the standard trading fee (e.g., 70 bps instead of 20 bps). The additional cost covers XLM account funding.

### Example: Claiming Tokens

After the order reaches `completed` status, the order response includes the claim transaction:

```json theme={null}
{
  "orderId": "123e4567-e89b-12d3-a456-426614174000",
  "status": "completed",
  "stellarClaimableBalanceId": "00000000abc123...",
  "stellarClaimTransaction": "AAAAAgAAAABk...base64_xdr..."
}
```

The user (or your application) signs `stellarClaimTransaction` and submits it to Stellar Horizon:

```javascript theme={null}
// Using Stellar SDK
const tx = TransactionBuilder.fromXDR(
  order.stellarClaimTransaction,
  networkPassphrase
);
tx.sign(userKeypair);
await server.submitTransaction(tx);
```

After submission, the user's wallet will have the trustline and the tokens.

<Warning>
  **The claim transaction uses the wallet's current sequence number.** If the user submits other transactions before claiming, the sequence number may become stale and the claim transaction will fail. In that case, your application can rebuild the claim transaction from the `stellarClaimableBalanceId` and asset info in the order response.
</Warning>
