> ## 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 & Reverse Quote

> Lock the exchange rate at quote time, or specify the exact amount the recipient receives

## 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.

**Reverse quotes** build on the same rate lock and invert which side of the swap you specify: instead of "I am sending 1 ETH, what do I get?", you ask "I need the recipient to get exactly 2500 USDC, what must I send?".

<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, and for paying invoices where the amount the recipient receives is fixed.
</Info>

<Note>
  Fixed rate is available for **standard (CEX)** and **private (multi-hop CEX)** routing. It is not available for DEX swaps — combining `fixed=true` with a DEX quote type returns `DEX_AND_FIXED_RATE_NOT_SUPPORTED` (422).

  Reverse quotes require a fixed rate. See [Reverse Quote](#reverse-quote) below.
</Note>

## How it fits in

Fixed rate and reverse quotes are **options on an existing flow**, not a separate flow. Follow the [Standard Swap](/developer-hub/swap-flows/standard-swap) or [Private Swap](/developer-hub/swap-flows/private-swap) guide as normal — fetching tokens, creating the order, sending the deposit, and polling status are all unchanged.

Only three things differ:

| Stage             | What changes                                                                                                                     |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `GET /quotes`     | Add `fixed=true` (and `amountType=receive` for a [reverse quote](#reverse-quote)). Pass `refundAddress` so it is validated early |
| `POST /exchanges` | `refundAddress` becomes **required**. Add `refundExtraId` on memo-bearing chains                                                 |
| Between the two   | The quote now expires — see [three deadlines](#three-deadlines) below                                                            |

The rest of this page covers only those differences.

## Fixed Rate

### Requesting a quote

Add `fixed=true` to `GET /quotes`. Standard and private routing work exactly the same way — `types` just selects which you get, and omitting it returns both.

<CodeGroup>
  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    amount: '1',
    from: '6689b73ec90e45f3b3e51566',       // ETH token id
    to:   '6689b73ec90e45f3b3e51558',       // USDC token id
    fixed: 'true',                          // request fixed-rate quotes
    types: 'standard,private',              // both support fixed rate; omit for the same result
    refundAddress: '0xYourRefundWallet',    // source-chain wallet, validated now
  });

  const { quotes } = await (await fetch(
    `https://api-partner.houdiniswap.com/v2/quotes?${params}`,
    { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } }
  )).json();

  const quote = quotes[0];
  console.log('Guaranteed out:', quote.amountOut);
  console.log('Rate locked until:', new Date(Number(quote.validUntil) * 1000));
  ```

  ```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"
  ```
</CodeGroup>

### Quote fields

Alongside the usual quote fields, a fixed-rate quote adds:

| Field        | Type    | Description                                                                                                               |
| ------------ | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `fixed`      | boolean | `true` confirms the quote is rate-locked. On a private route this means **both** hops are locked                          |
| `validUntil` | string  | Rate lock expiry as a **Unix timestamp in seconds**. On a private route this is the **earlier** of the two hops' expiries |
| `amountType` | string  | Echoes the requested mode — `"send"` or `"receive"`                                                                       |

### Three deadlines

Fixed rate orders carry three independent time limits. Partners routinely conflate them, so handle each one explicitly:

| Clock               | Field                                     | Typical window                              | What happens when it passes                                                                               |
| ------------------- | ----------------------------------------- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Quote freshness** | none — measured from the quote's creation | **60 seconds**                              | `POST /exchanges` returns 422 `VALIDATION_ERROR` — `The provided quote has expired, max age is N seconds` |
| **Rate lock**       | `validUntil`                              | 3–20 minutes, provider-dependent            | `POST /exchanges` returns 422 `FIXED_RATE_QUOTE_EXPIRED`                                                  |
| **Deposit window**  | `expires`                                 | typically 7–15 minutes, set by the provider | The deposit is no longer guaranteed at the locked rate                                                    |

### Creating the order

Identical to a normal swap, except `refundAddress` is **required** — omitting it returns 422 `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE`.

```json theme={null}
{
  "quoteId": "69af9e02f9c5affabcaccc14",
  "addressTo": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
  "refundAddress": "rYourXrpRefundAddress",
  "refundExtraId": "1234567890"
}
```

`refundExtraId` is the memo or tag for `refundAddress`. It applies only when the **source** chain uses one (XRP, Stellar, TON, Cosmos) — the example above refunds to XRP. Omit it everywhere else.

<Warning>
  `refundExtraId` is **not enforced**, even on chains that require one. If you omit it on a memo-bearing chain, a refund can land in the exchange's omnibus account and will need manual recovery. Always send it when the source chain has a memo concept.
</Warning>

The order response adds `fixed: true` and echoes `refundAddress`. Everything else — `depositAddress`, `inAmount`, `expires`, status polling — behaves exactly as described in the [Standard Swap](/developer-hub/swap-flows/standard-swap) guide.

<Note>
  The order response does **not** echo `validUntil`. Once the order exists the rate is locked, so the only deadline that still matters is `expires`. Keep the quote's `validUntil` on your side if you want to display a countdown.
</Note>

<Note>
  **Fixed rate never silently re-quotes.** The rate is bound to the route you were quoted. If anything changes between quote and exchange — the provider loses fixed-rate capability, a token or rate combination is disabled, a leg is excluded — you get `FIXED_RATE_QUOTE_EXPIRED` rather than a substituted rate.
</Note>

### When no quotes are returned

An empty `quotes` array is a normal outcome, not an error — a fixed-rate route has to satisfy constraints a floating one does not. This is far more likely on **private** routing, where every constraint must hold for *both* hops:

<AccordionGroup>
  <Accordion title="The provider supports and has fixed rate enabled" icon="link">
    Only about half the CEX partners offer fixed rate, and each is independently toggleable. On a private route both hops must qualify, and the second hop is further restricted to providers that can issue automatic refunds.
  </Accordion>

  <Accordion title="The amount is under the fixed-rate USD ceiling" icon="chart-line">
    Fixed rate carries a separate, lower USD cap than floating rate — applied per hop on a private route. Fixed-rate books are also generally narrower, so a `max` that works for a floating quote may not for a fixed one.
  </Accordion>

  <Accordion title="Private routing only: the first hop is fast enough" icon="clock">
    A rate lock cannot outlive a slow first hop, so private fixed candidates whose first hop is estimated to take too long are dropped.
  </Accordion>

  <Accordion title="Private routing only: the intermediate token is enabled" icon="shuffle">
    The bridge token used between the hops can be disabled for the private-fixed combination specifically, without affecting floating private swaps on the same pair.
  </Accordion>
</AccordionGroup>

***

## Reverse Quote

By default, `amount` is what the user **sends** and the API computes what they receive. A reverse quote flips this: set `amountType=receive` and `amount` becomes what the recipient must **receive**, with the API computing the required deposit.

This is what you want when the amount the recipient receives is the fixed constraint — paying an invoice, settling a fixed-price order, or topping an account up to an exact balance.

<Warning>
  **Reverse quotes require `fixed=true`.** There is no floating variant — a floating rate moves between quote and settlement, so a guaranteed output is a promise it cannot keep. Requesting `amountType=receive` without `fixed=true` returns 422 `EXACT_OUT_REQUIRES_FIXED_RATE`.
</Warning>

<CodeGroup>
  ```javascript JavaScript theme={null}
  const params = new URLSearchParams({
    amount: '2500',                         // the recipient must receive exactly 2500 USDC
    from: '6689b73ec90e45f3b3e51566',       // ETH token id
    to:   '6689b73ec90e45f3b3e51558',       // USDC token id
    fixed: 'true',                          // required for a reverse quote
    amountType: 'receive',                  // `amount` is the receive side
    refundAddress: '0xYourRefundWallet',
  });

  const { quotes } = await (await fetch(
    `https://api-partner.houdiniswap.com/v2/quotes?${params}`,
    { headers: { 'Authorization': `${API_KEY}:${API_SECRET}` } }
  )).json();

  const quote = quotes[0];
  console.log('Recipient receives:', quote.amountOut);  // exactly 2500 — the guaranteed side
  console.log('User must send:',     quote.amountIn);   // computed by the API
  ```

  ```bash cURL theme={null}
  curl -X GET "https://api-partner.houdiniswap.com/v2/quotes?amount=2500&from=6689b73ec90e45f3b3e51566&to=6689b73ec90e45f3b3e51558&fixed=true&amountType=receive&refundAddress=0xYourRefundWallet" \
    -H "Authorization: your_api_key:your_api_secret"
  ```
</CodeGroup>

### Response changes

| Field               | Default (`amountType=send`) | Reverse quote (`amountType=receive`)                                         |
| ------------------- | --------------------------- | ---------------------------------------------------------------------------- |
| `amountIn`          | What you asked for          | **Computed** — what the user must send                                       |
| `amountOut`         | Computed                    | **Equals the `amount` you requested** — the guaranteed side                  |
| `min` / `max`       | Send-side bounds            | Still **send-side** bounds                                                   |
| `minOut` / `maxOut` | Not returned                | **Receive-side** bounds — validate the user's requested amount against these |
| Default `sort`      | `amountOut`                 | `amountIn`                                                                   |
| Default `sortOrder` | `desc`                      | `asc` — cheapest deposit first                                               |

<Note>
  The sort defaults flip deliberately. On a reverse quote every route delivers the same `amountOut`, so routes are ranked by the *smallest* `amountIn` — the least the user has to send. If you pass `sort` explicitly, it is respected in both modes.
</Note>

<Warning>
  Validate the requested output amount against `minOut` / `maxOut`, not `min` / `max`.
</Warning>

### Provider support

Quoting backwards from the output side is something not every CEX partner can do. Combined with the fixed-rate requirement, the eligible provider set is meaningfully smaller than for a plain floating quote — and on a private route **both** hops must qualify. Expect fewer routes, and handle an empty `quotes` array the same way — see [when no quotes are returned](#when-no-quotes-are-returned).

<Note>
  Reverse quotes are not available on batch orders. `POST /exchanges/multi` has no `amountType` field — see [Multi-Swap](/developer-hub/swap-flows/multi-swap).
</Note>

***

## Error Codes

| Code                                                                        | HTTP    | When                                                                                                                                                    | What to do                                                                                                                                    |
| --------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `REFUND_ADDRESS_REQUIRED_FOR_FIXED_RATE`                                    | 422     | `/exchanges` called without `refundAddress`                                                                                                             | Collect and validate the refund address before the user submits                                                                               |
| `ADDRESS_REFUND_INVALID_FOR_CHAIN`                                          | 422     | `refundAddress` fails source-chain format validation, or exceeds the maximum length                                                                     | Validate against the **source** chain's rules — including Solana, XRP and other non-EVM formats. Pass it at quote time so this surfaces early |
| `ADDRESS_REFUND_SANCTIONED`                                                 | 422     | `refundAddress` failed compliance screening                                                                                                             | Collect a different refund address                                                                                                            |
| `FIXED_RATE_QUOTE_EXPIRED`                                                  | 422     | `validUntil` has passed, or the route lost fixed-rate capability after the quote was issued. On a private route, either hop expiring is enough          | Fetch a new quote. There is no silent substitution, so if it recurs a provider may have disabled fixed rate — offer a floating quote instead  |
| `VALIDATION_ERROR` — `The provided quote has expired, max age is N seconds` | 422     | More than 60 seconds elapsed between `GET /quotes` and `POST /exchanges`. Separate from `validUntil`, and fires even while the rate lock is still valid | Re-fetch and submit immediately. Request the quote at confirmation time, not at screen load                                                   |
| `EXACT_OUT_REQUIRES_FIXED_RATE`                                             | 422     | `amountType=receive` without `fixed=true`                                                                                                               | Add `fixed=true` — reverse quotes have no floating variant                                                                                    |
| `DEX_AND_FIXED_RATE_NOT_SUPPORTED`                                          | 422     | `fixed=true` combined with a DEX quote type                                                                                                             | Use `types=standard` and/or `types=private`                                                                                                   |
| `EXACT_OUT_NOT_SUPPORTED_FOR_DEX`                                           | 422     | `amountType=receive` on a DEX-only quote request                                                                                                        | Same — reverse quotes are CEX only                                                                                                            |
| `UNABLE_TO_CREATE_REFUND_LEG`                                               | 422     | Private fixed order — the refund path could not be set up, so the order was not created and no funds moved                                              | Retry; this depends on provider availability. If it persists, fall back to a floating private swap or a standard fixed swap                   |
| `FIXED_RATE_NOT_ALLOWED`                                                    | **403** | Fixed-rate quotes are not enabled for your partner account                                                                                              | Contact your Houdini representative                                                                                                           |
| `FLOATING_RATE_NOT_ALLOWED`                                                 | **403** | Floating-rate quotes are not enabled for your partner account                                                                                           | Contact your Houdini representative                                                                                                           |

<Note>
  **An order that reaches `REFUNDED`** failed after the rate lock was accepted — for example the CEX rejected the transaction. Refund handling depends on the provider: some return funds to `refundAddress` automatically, others require the user to [contact support](/faqs/contact-support). Surface the failure to the user either way.
</Note>

***

## Next Steps

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

  <Card title="Private Swap" 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>
