> ## Documentation Index
> Fetch the complete documentation index at: https://docs.houdini.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?".

> [!NOTE]
>
> **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.

> [!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.

## How it fits in

Fixed rate and reverse quotes are **options on an existing flow**, not a separate flow. Follow the [Standard Swap](https://docs.houdini.com/developer-hub/swap-flows/standard-swap.md) or [Private Swap](https://docs.houdini.com/developer-hub/swap-flows/private-swap.md) 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.

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

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

The order response adds `fixed: true` and echoes `refundAddress`. Everything else — `depositAddress`, `inAmount`, `expires`, status polling — behaves exactly as described in the [Standard Swap](https://docs.houdini.com/developer-hub/swap-flows/standard-swap.md) 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]
>
> **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.

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

> [accordion] The provider supports and has fixed rate enabled
>
> 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] The amount is under the fixed-rate USD ceiling
>
> 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] Private routing only: the first hop is fast enough
>
> 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] Private routing only: the intermediate token is enabled
>
> 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.

***

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

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

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

> [!WARNING]
>
> Validate the requested output amount against `minOut` / `maxOut`, not `min` / `max`.

### 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 work on Split orders too: set `amountType: "receive"` together with `fixed: true` on the order in `POST /exchanges/multi` — see [Split](https://docs.houdini.com/developer-hub/swap-flows/split.md).

***

## 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](https://docs.houdini.com/faqs/contact-support.md). Surface the failure to the user either way.

***

## Next Steps

> [card] [Standard Swap](https://docs.houdini.com/developer-hub/swap-flows/standard-swap.md)
>
> Fast single-hop CEX swaps without rate lock

> [card] [Private Swap](https://docs.houdini.com/developer-hub/swap-flows/private-swap.md)
>
> Multi-hop privacy swaps via CEX routing

> [card] [Order Lifecycle](https://docs.houdini.com/developer-hub/core-concepts/order-lifecycle.md)
>
> Understand all order statuses

> [card] [Error Handling](https://docs.houdini.com/developer-hub/troubleshooting/errors.md)
>
> Handle errors and edge cases


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.