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

# Fixed Rate Swap

> Lock the exchange rate at quote time — guaranteed output amount regardless of market movement

## Overview

Fixed rate swaps lock the exchange rate at the moment you request a quote. The user receives exactly the quoted `amountOut` regardless of market movement between quote time and swap execution — as long as the exchange is created before the rate lock expires.

<Info>
  **Best For**: Partners and users who want price certainty and are willing to plan around a short rate-lock window. Ideal for high-value swaps where slippage risk is unacceptable.
</Info>

<Warning>
  Fixed rate is only available for **standard (CEX) swaps**. It cannot be combined with private/anonymous swaps or DEX swaps. Attempting either will return a 422 error.
</Warning>

## Key Features

<CardGroup cols={2}>
  <Card title="Guaranteed Output" icon="lock">
    User receives exactly `amountOut` as quoted — no slippage, no surprises at settlement
  </Card>

  <Card title="Rate Lock Window" icon="clock">
    `validUntil` timestamp gives you a window to create the exchange before the rate expires
  </Card>

  <Card title="Refund on Failure" icon="rotate-left">
    If the swap fails after the rate is locked, the `refundAddress` is used for any potential refund — though refund handling depends on the provider
  </Card>

  <Card title="Provider-Bound Rate" icon="building-columns">
    The rate is held by a specific CEX provider via `rateId` — no silent fallback to a different provider or rate
  </Card>
</CardGroup>

## How It Works

<Steps>
  <Step title="Get Tokens">
    Fetch CEX-supported tokens and cache to your DB. Note each token's `id`.
  </Step>

  <Step title="Get a Fixed Rate Quote">
    Call `GET /quotes` with `fixed=true`. The response includes a `quoteId`, guaranteed `amountOut`, and `validUntil` expiry.
  </Step>

  <Step title="Create the Exchange">
    Call `POST /exchanges` with the `quoteId`, `addressTo`, and a `refundAddress`. The `refundAddress` is required for fixed rate orders.
  </Step>

  <Step title="Send Deposit">
    Send exactly `inAmount` of the input token to the `depositAddress` returned in the order.
  </Step>

  <Step title="Monitor Status">
    Poll `GET /orders/{houdiniId}` until `statusLabel` is `FINISHED`. If the swap fails, status may become `REFUNDED` — refund handling depends on the provider.
  </Step>
</Steps>

## Integration Guide

### Step 1: Get Tokens

Token fetching is the same as for standard swaps — paginate through `/v2/tokens?hasCex=true` and cache the results. See [Standard Swap — Step 1](/developer-hub/swap-flows/standard-swap#step-1-get-tokens) for the full code example.

Use each token's `id` field (not `symbol`) in the `/quotes` request.

### Step 2: Get a Fixed Rate Quote

Add `fixed=true` to your `/quotes` request. The API will automatically restrict results to standard (CEX) quotes only — you do not need to pass `types=standard` separately.

Pass `refundAddress` at quote time so it is validated early. It must be a valid address on the **source chain**.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    amount: '1',
    from: '6689b73ec90e45f3b3e51566',       // ETH token id from /tokens
    to:   '6689b73ec90e45f3b3e51558',       // USDC token id from /tokens
    fixed: 'true',                          // request fixed-rate quotes
    refundAddress: '0xYourRefundWallet',    // source-chain wallet for refunds
  });

  const response = await fetch(
    `https://api-partner.houdiniswap.com/v2/quotes?${params}`,
    {
      headers: {
        'Authorization': `${API_KEY}:${API_SECRET}`,
        'x-user-ip': userIp,
        'x-user-agent': userAgent,
        'x-user-timezone': userTimezone
      }
    }
  );

  const { quotes } = await response.json();

  // All returned quotes will be fixed-rate standard quotes
  const quote = quotes[0];
  console.log('Guaranteed out:', quote.amountOut);
  console.log('Rate locked until:', quote.validUntil);
  console.log('Quote ID:', quote.quoteId);  // needed for /exchanges
  ```

  ```bash cURL theme={null}
  curl -X GET "https://api-partner.houdiniswap.com/v2/quotes?amount=1&from=6689b73ec90e45f3b3e51566&to=6689b73ec90e45f3b3e51558&fixed=true&refundAddress=0xYourRefundWallet" \
    -H "Authorization: your_api_key:your_api_secret" \
    -H "x-user-ip: 192.168.1.1" \
    -H "x-user-agent: Mozilla/5.0..." \
    -H "x-user-timezone: America/New_York"
  ```
</CodeGroup>

### Fixed Rate Quote Response

```json theme={null}
{
  "quotes": [
    {
      "quoteId": "69af9e02f9c5affabcaccc14",
      "type": "standard",
      "swap": "cc",
      "swapName": "Coincraddle",
      "logoUrl": "https://api.houdiniswap.com/assets/logos/coincraddle.jpg",
      "amountIn": 1,
      "amountOut": 2456.78,
      "amountOutUsd": 2456.78,
      "min": 0.098169,
      "max": 4688.581529,
      "duration": 30,
      "fixed": true,
      "rateId": "abc123providerratelockid",
      "validUntil": "2025-12-25T06:28:00.000Z",
      "rewardsAvailable": true
    }
  ],
  "total": 3
}
```

**Key fixed-rate quote fields:**

| Field        | Type              | Description                                                                  |
| ------------ | ----------------- | ---------------------------------------------------------------------------- |
| `quoteId`    | string            | Pass to `/exchanges` to create the order                                     |
| `fixed`      | boolean           | `true` confirms this is a fixed-rate quote                                   |
| `rateId`     | string            | Provider-internal rate lock identifier — carried automatically via `quoteId` |
| `validUntil` | string (ISO 8601) | Hard deadline — create the exchange before this timestamp                    |
| `amountOut`  | number            | Guaranteed output amount                                                     |

<Warning>
  `validUntil` is a hard deadline with no grace period. Check `Date.now() < new Date(quote.validUntil)` before calling `/exchanges`. If you miss the window, re-fetch a new quote.
</Warning>

### Step 3: Create the Exchange

Pass `quoteId`, `addressTo`, and `refundAddress` to `POST /exchanges`. The `refundAddress` is **required** for fixed rate orders — omitting it returns a 422 error.

<CodeGroup>
  ```javascript JavaScript theme={null}
  // Always check the rate is still valid before submitting
  if (Date.now() >= new Date(quote.validUntil).getTime()) {
    throw new Error('Rate expired — fetch a new quote');
  }

  const response = await fetch('https://api-partner.houdiniswap.com/v2/exchanges', {
    method: 'POST',
    headers: {
      'Authorization': `${API_KEY}:${API_SECRET}`,
      'Content-Type': 'application/json',
      'x-user-ip': userIp,
      'x-user-agent': userAgent,
      'x-user-timezone': userTimezone
    },
    body: JSON.stringify({
      quoteId: '69af9e02f9c5affabcaccc14',       // from /quotes
      addressTo: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
      refundAddress: '0xYourRefundWallet'         // required for fixed rate
    })
  });

  const order = await response.json();
  console.log('Order ID:', order.houdiniId);
  console.log('Deposit to:', order.depositAddress);
  console.log('Send:', order.inAmount, order.inSymbol);
  console.log('Rate locked:', order.fixed, '— guaranteed out:', order.outAmount);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api-partner.houdiniswap.com/v2/exchanges" \
    -H "Authorization: your_api_key:your_api_secret" \
    -H "Content-Type: application/json" \
    -H "x-user-ip: 192.168.1.1" \
    -H "x-user-agent: Mozilla/5.0..." \
    -H "x-user-timezone: America/New_York" \
    -d '{
      "quoteId": "69af9e02f9c5affabcaccc14",
      "addressTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
      "refundAddress": "0xYourRefundWallet"
    }'
  ```
</CodeGroup>

### Exchange Response

```json theme={null}
{
  "houdiniId": "iBQMRX3xvXrFMGQi71ogo9",
  "created": "2025-12-25T06:13:46.673Z",
  "expires": "2025-12-25T06:43:46.673Z",
  "depositAddress": "0x7364a0b6c55004427a4a7c26355ce9c75ef56194",
  "receiverAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
  "refundAddress": "0xYourRefundWallet",
  "fixed": true,
  "validUntil": "2025-12-25T06:28:00.000Z",
  "anonymous": false,
  "status": -1,
  "statusLabel": "NEW",
  "inAmount": 1,
  "inSymbol": "ETH",
  "outAmount": 2456.78,
  "outSymbol": "USDC",
  "eta": 3,
  "swapName": "Coincraddle"
}
```

**Key response fields:**

| Field                   | Type              | Description                                                                                     |
| ----------------------- | ----------------- | ----------------------------------------------------------------------------------------------- |
| `houdiniId`             | string            | Unique order ID — use for status polling                                                        |
| `depositAddress`        | string            | Send input funds here                                                                           |
| `inAmount` / `inSymbol` | number / string   | Exact amount and token to send                                                                  |
| `expires`               | string (ISO 8601) | Deposit deadline (typically 30 minutes)                                                         |
| `fixed`                 | boolean           | Confirms the order is rate-locked                                                               |
| `validUntil`            | string (ISO 8601) | Rate lock expiry echoed from the quote                                                          |
| `refundAddress`         | string            | Address used for refunds if the swap fails — whether a refund is issued depends on the provider |

<Note>
  The rate is bound to a single provider via the `rateId` embedded in the `quoteId`. If that provider loses fixed-rate capability between quote and exchange time, you will receive `FIXED_RATE_QUOTE_EXPIRED` — there is no silent fallback to a different provider or rate.
</Note>

<Warning>
  Send exactly `inAmount` of `inSymbol` to `depositAddress` before `expires`.
</Warning>

### Step 4: Send Deposit

After creating the order, send the input tokens to `depositAddress`. The status advances automatically once the deposit is detected on-chain.

<Warning>
  Send funds before the `expires` deadline. If funds arrive after the deadline, the fixed rate is no longer guaranteed and the order may not be processed. Please [contact our customer support](/faqs/contact-support) for assistance.
</Warning>

### Step 5: Monitor Order Status

Poll `GET /orders/{houdiniId}` to track progress, or subscribe via the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates) for real-time updates:

<CodeGroup>
  ```javascript JavaScript theme={null}
  const response = await fetch(
    `https://api-partner.houdiniswap.com/v2/orders/${order.houdiniId}`,
    {
      headers: {
        'Authorization': `${API_KEY}:${API_SECRET}`,
        'x-user-ip': userIp,
        'x-user-agent': userAgent,
        'x-user-timezone': userTimezone
      }
    }
  );

  const status = await response.json();
  console.log('Status:', status.statusLabel);
  // WAITING     — awaiting deposit
  // CONFIRMING  — deposit detected, awaiting confirmations
  // EXCHANGING  — CEX processing the swap at locked rate
  // FINISHED    — swap complete, guaranteed amount sent
  // FAILED      — swap failed
  // REFUNDED    — swap failed; refund handling depends on provider
  ```

  ```bash cURL theme={null}
  curl -X GET "https://api-partner.houdiniswap.com/v2/orders/iBQMRX3xvXrFMGQi71ogo9" \
    -H "Authorization: your_api_key:your_api_secret" \
    -H "x-user-ip: 192.168.1.1" \
    -H "x-user-agent: Mozilla/5.0..." \
    -H "x-user-timezone: America/New_York"
  ```
</CodeGroup>

### Status Progression

```
NEW / WAITING
  ↓ Deposit sent and detected
CONFIRMING
  ↓ Blockchain confirmations received
EXCHANGING
  ↓ CEX processes the swap at the locked rate
FINISHED    ← guaranteed amountOut delivered
  (or)
REFUNDED    ← swap failed; refund handling depends on provider
```

<Tip>
  Poll every 30 seconds. Fixed rate swaps follow the same timeline as standard swaps (typically 3–30 minutes). For real-time updates without polling, use the [WebSocket API](/developer-hub/core-concepts/websocket-order-updates).
</Tip>

***

## Error Codes

| Code                                     | HTTP | When                                                                                           |
| ---------------------------------------- | ---- | ---------------------------------------------------------------------------------------------- |
| `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE` | 422  | `/exchanges` called without `refundAddress`                                                    |
| `ANONYMOUS_AND_FIXED_RATE_NOT_SUPPORTED` | 422  | `fixed=true` combined with private/anonymous swap mode                                         |
| `DEX_AND_FIXED_RATE_NOT_SUPPORTED`       | 422  | `fixed=true` combined with a DEX quote type                                                    |
| `FIXED_RATE_QUOTE_EXPIRED`               | 422  | `validUntil` has passed, or the provider lost fixed-rate capability after the quote was issued |
| `ADDRESS_INVALID_FOR_CHAIN`              | 422  | `refundAddress` fails format validation for the source chain                                   |

***

## Best Practices

<AccordionGroup>
  <Accordion title="Always check validUntil before creating the exchange" icon="clock">
    Build in a buffer — don't wait until the last second. Check `Date.now() < new Date(quote.validUntil)` immediately before calling `/exchanges`. If the window is too short, fetch a fresh quote rather than risk `FIXED_RATE_QUOTE_EXPIRED`.
  </Accordion>

  <Accordion title="refundAddress must be on the source chain" icon="link">
    The refund wallet is validated against source-chain address format rules (including Solana, XRP, and other non-EVM chains). Pass it at quote time so validation happens early — not when you create the exchange.
  </Accordion>

  <Accordion title="No fallback if the provider becomes unavailable" icon="triangle-exclamation">
    Fixed rate is tied to one provider. If that provider disables fixed rate or goes down between your quote and exchange calls, the order fails with `FIXED_RATE_QUOTE_EXPIRED`. Re-quoting will select a new provider if one is available.
  </Accordion>

  <Accordion title="Show the rate lock to the user" icon="eye">
    Display `amountOut` and `validUntil` prominently in your UI before the user confirms. This sets clear expectations about the guaranteed output and how long they have to act.
  </Accordion>
</AccordionGroup>

***

## Common Issues

<AccordionGroup>
  <Accordion title="FIXED_RATE_QUOTE_EXPIRED">
    **Cause**: The exchange was created after `validUntil`, or the provider lost fixed-rate capability between quote and exchange time.

    **Solution**: Fetch a new quote. If the error recurs, the provider may have temporarily disabled fixed rate — try again after a short delay or present the user with a floating-rate quote as a fallback.
  </Accordion>

  <Accordion title="REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE">
    **Cause**: `refundAddress` was not included in the `/exchanges` request body.

    **Solution**: Always include `refundAddress` when creating a fixed rate exchange. Collect and validate the address before the user submits.
  </Accordion>

  <Accordion title="Order failed / status is REFUNDED">
    **Cause**: The swap failed after the rate lock was accepted — for example, the CEX rejected the transaction.

    **Solution**: Refund handling depends on the provider. Some providers automatically return funds to `refundAddress`; others require the user to contact customer support. Inform the user of the failure and direct them to support if a refund is not issued automatically.
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Standard Swaps" icon="bolt" href="/developer-hub/swap-flows/standard-swap">
    Fast single-hop CEX swaps without rate lock
  </Card>

  <Card title="Private Swaps" icon="lock" href="/developer-hub/swap-flows/private-swap">
    Multi-hop privacy swaps via CEX routing
  </Card>

  <Card title="Order Lifecycle" icon="rotate" href="/developer-hub/core-concepts/order-lifecycle">
    Understand all order statuses
  </Card>

  <Card title="Error Handling" icon="triangle-exclamation" href="/developer-hub/troubleshooting/errors">
    Handle errors and edge cases
  </Card>
</CardGroup>
