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

# Route-wide Slippage

> Set one slippage budget for a whole route and get a single minimum output for it, held by the transaction and, on cross-chain routes, by the bridge.

Route-wide slippage turns `slippage` into one budget for the whole route instead of a tolerance for each step. You send `slippageScope=route`, and LI.FI returns a single minimum output for the route as `toAmountMin`. The transaction is built so the user receives at least that amount. If that can't happen, the transaction reverts, or the transfer is reported as not fulfilled.

It is opt-in per request. Omitting `slippageScope`, or sending `slippageScope=step`, keeps today's per-step behavior exactly as it is.

***

## Per-step vs route-wide slippage

`slippage` can apply in two ways, chosen per request with `slippageScope`:

* **Per-step (`step`, the default).** `slippage` is the tolerance for each step that carries price risk. Each step gets its own minimum.
* **Route-wide (`route`).** `slippage` is one budget for the whole route. LI.FI splits it across those steps and returns a single minimum for the route.

| | Per-step (default) | Route-wide |
| - | - | - |
| What `slippage` means | Tolerance for each step | Budget for the whole route |
| `toAmountMin` | Derived from each step's minimum | One floor: `ceil(expected output × (1 − slippage))` |
| Swap + a bridge that prices its own output, at `0.01` | Each step can use up to 1% | The route as a whole uses at most 1% |
| When a rebuild prices worse | Accepted within each step's tolerance | Built only if it still reaches the floor; the floor is never lowered |

## How it works

Route-wide slippage works with three numbers:

* **Budget (`slippage`).** The total slippage you allow for the route, as a decimal. `0.01` is 1%.
* **Expected output (`routeSlippage.referenceToAmount`).** What the route is expected to deliver when it is quoted.
* **Floor (`routeSlippage.minimumToAmount`).** `ceil(referenceToAmount × (1 − slippage))`, in the destination token's base units. It is also returned as `toAmountMin`.

LI.FI splits the budget across the steps that carry price risk: swaps, and bridges that change the token or price it themselves. Fee collection, wrapping and same-token bridges get no share.

LI.FI only returns routes whose quoted amounts can reach the floor. The floor then stays fixed for the life of the route. Every build of the transaction must still reach it, or it is refused.

**1. Request**

Add `slippageScope=route` to a [quote](https://docs.li.fi/api-reference/get-a-quote-for-a-token-transfer.md) or [routes](https://docs.li.fi/api-reference/advanced/get-a-set-of-routes-for-a-request-that-describes-a-transfer-of-tokens.md) request.

**2. Show the floor**

Show `toAmountMin` as the minimum the user receives. The route and its steps carry a `routeSlippage` object.

**3. Build**

A quote already includes `transactionRequest`. For a route, post each step unchanged to [`/v1/advanced/stepTransaction`](https://docs.li.fi/api-reference/advanced/populate-a-step-with-transaction-data), including `routeSlippage`. Do not drop this field when reconstructing or serializing a step: a step submitted without it is built with per-step slippage instead.

**4. Track**

Poll [`/v1/status`](https://docs.li.fi/api-reference/check-the-status-of-a-cross-chain-transfer). Once the transfer is final, `routeSlippage.outcome` tells you whether the floor was met.

***

## Supported routes

Route-wide slippage covers EVM routes with at most one swap:

| Route | How the floor is enforced | When the floor can't be met |
| - | - | - |
| A swap on one EVM chain, optionally with LI.FI fee collection or token wrapping | The swap's minimum output is the floor. `settlement` is `atomic`. | The transaction reverts. The user keeps the input token and pays only gas. |
| A bridge between two EVM chains that delivers to the recipient, with or without one swap before it on the source chain | The source transaction holds the swap to a checkpoint: the amount it must hand to the bridge. From there the bridge holds the floor, as described in [cross-chain settlement](#cross-chain-settlement). `settlement` is `conditional`. | Below the checkpoint, the source transaction reverts. After that, a bridge that can't deliver its minimum refunds or recovers instead, and `/v1/status` reports `NOT_FULFILLED`. |

These routes aren't protected:

* Routes with a swap or contract call on the destination chain after the bridge
* Routes with more than one swap
* Routes that start or end on a non-EVM chain, such as Solana, Bitcoin, Sui or Tron
* Tokens that charge a fee on transfer
* Routes through Allbridge, Hyperliquid deposits or Smart Deposits, and same-chain intent-based swaps
* Exact-output quotes (`/v1/quote/toAmount`) and contract-call quotes (`/v1/quote/contractCall` and `/v1/quote/contractCalls`). These reject `slippageScope=route` with a `400`.

A route that can't be protected is never downgraded to per-step slippage. It is left out of the response, with a reason that starts with `ROUTE_SLIPPAGE_UNSUPPORTED`:

* Routes requests list it in `unavailableRoutes.filteredOut`.
* If no route can be protected, a quote request returns a `404` that lists the reasons in `errors.filteredOut`.

### Cross-chain settlement

LI.FI's contracts enforce amounts on the source chain only. On a cross-chain route, the source transaction holds the swap to the checkpoint, and the bridge then delivers on the destination chain. How the floor is held from there depends on the bridge:

* **Mayan, Across and Stargate V2.** The bridge's own order carries at least the floor, so the bridge won't settle under it. A Mayan order carries a minimum output, Across fills an exact output amount, and Stargate V2 reverts the source transaction under its minimum. A same-token Mayan MCTP transfer carries no minimum, because it delivers the burned amount minus fees that are fixed in the transaction.
* **Other bridges.** The floor isn't in the transaction. The bridge is quoted for the amount the source transaction guarantees it, and the transaction is built only if the bridge's own minimum for that amount reaches the floor. Delivery then relies on the bridge honoring that minimum.

`/v1/status` judges every protected transfer against the floor. A refund or the delivery of another token is reported as `NOT_FULFILLED` with `REFUNDED` or `DIFFERENT_TOKEN`. `BELOW_FLOOR` isn't an expected way for a transfer to settle: it means the bridge delivered the committed token under the minimum it was built with. It is reported so that such a delivery is never counted as `FULFILLED`.

***

## Request a route-protected quote

`/v1/quote` returns one step with the transaction already built against the floor:

```bash curl theme={"system"}
curl --request GET \
  --url 'https://li.quest/v1/quote?fromChain=42161&toChain=42161&fromToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&toToken=0x82aF49447D8a07e3bd95BD0d56f35241523fBab1&fromAmount=1000000000&fromAddress=0xYOUR_USER_ADDRESS&integrator=YOUR_INTEGRATOR_NAME&slippage=0.01&slippageScope=route' \
  --header 'x-lifi-api-key: YOUR_API_KEY'
```

```ts TypeScript theme={"system"}
const params = new URLSearchParams({
  fromChain: '42161',                                       // Arbitrum
  toChain: '42161',
  fromToken: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',  // USDC
  toToken: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',    // WETH
  fromAmount: '1000000000',                                 // 1,000 USDC (6 decimals)
  fromAddress: '0xYOUR_USER_ADDRESS',
  integrator: 'YOUR_INTEGRATOR_NAME',
  slippage: '0.01',                                         // 1% for the whole route
  slippageScope: 'route',
})

const quote = await fetch(`https://li.quest/v1/quote?${params}`, {
  headers: { 'x-lifi-api-key': process.env.LIFI_API_KEY! },
}).then((res) => res.json())

console.log(quote.estimate.toAmountMin)                     // the floor
console.log(quote.routeSlippage.minimumToAmount)            // the same floor
```

To compare several routes, set `slippageScope` in the routes request options:

```bash curl theme={"system"}
curl --request POST \
  --url 'https://li.quest/v1/advanced/routes' \
  --header 'Content-Type: application/json' \
  --header 'x-lifi-api-key: YOUR_API_KEY' \
  --data '{
    "fromChainId": 42161,
    "fromAmount": "1000000000",
    "fromTokenAddress": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
    "fromAddress": "0xYOUR_USER_ADDRESS",
    "toChainId": 8453,
    "toTokenAddress": "0x0000000000000000000000000000000000000000",
    "options": {
      "integrator": "YOUR_INTEGRATOR_NAME",
      "slippage": 0.01,
      "slippageScope": "route"
    }
  }'
```

```ts TypeScript theme={"system"}
const { routes, unavailableRoutes } = await fetch('https://li.quest/v1/advanced/routes', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-lifi-api-key': process.env.LIFI_API_KEY!,
  },
  body: JSON.stringify({
    fromChainId: 42161,                                           // Arbitrum
    fromAmount: '1000000000',                                     // 1,000 USDC
    fromTokenAddress: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
    fromAddress: '0xYOUR_USER_ADDRESS',
    toChainId: 8453,                                              // Base
    toTokenAddress: '0x0000000000000000000000000000000000000000', // ETH
    options: {
      integrator: 'YOUR_INTEGRATOR_NAME',
      slippage: 0.01,
      slippageScope: 'route',
    },
  }),
}).then((res) => res.json())

// Routes that can't be protected are listed here, with a ROUTE_SLIPPAGE_* reason
console.log(unavailableRoutes.filteredOut)
```

`slippage` must be a decimal from `0` up to, but not including, `1`, with at most eight decimal places. A value that needs more precision is rejected, not rounded, because rounding would move the floor. If you omit `slippage`, the budget is the default slippage LI.FI would otherwise apply.

***

## Read the response

On a route-protected response:

* `toAmountMin` is the floor.
* `toAmount` is the current estimate. It can change between requests while the floor doesn't.
* The step's `action.slippage` is the whole budget.
* Each included step's `action.slippage` is its share of the budget.

The route and each of its steps carry the `routeSlippage` commitment:

| Field | Description |
| - | - |
| `scope` | Always `route` |
| `slippage` | The budget, as a decimal string such as `"0.01"` |
| `referenceToAmount` | The expected output the floor was computed from, in the destination token's base units |
| `minimumToAmount` | The floor: `ceil(referenceToAmount × (1 − slippage))` |
| `toChainId`, `toTokenAddress` | The chain and token the floor applies to |
| `toAddress` | The recipient the delivery is measured at |
| `settlement` | `atomic` for a single transaction, `conditional` for a cross-chain transfer |
| `checkpoints` | The amounts enforced along the way. One entry for `atomic`, the floor itself. For `conditional`, two entries: the source checkpoint, then the floor. |

A same-chain example, with 1,000 USDC to WETH at a 1% budget:

```json theme={"system"}
{
  "estimate": {
    "toAmount": "400000000000000000",
    "toAmountMin": "396000000000000000"
  },
  "routeSlippage": {
    "scope": "route",
    "slippage": "0.01",
    "referenceToAmount": "400000000000000000",
    "minimumToAmount": "396000000000000000",
    "toChainId": 42161,
    "toTokenAddress": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
    "toAddress": "0xYOUR_USER_ADDRESS",
    "settlement": "atomic",
    "checkpoints": [
      {
        "stepId": "0x48f0a2f93b0d0a9dab992d07c46bca38516c945101e8f8e08ca42af05b9e6aa9",
        "chainId": 42161,
        "tokenAddress": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
        "minimumAmount": "396000000000000000"
      }
    ]
  }
}
```

***

## Build and send the transaction

A quote already includes `transactionRequest`, built against the floor. Send it like any other quote.

For a route, post each step to [`/v1/advanced/stepTransaction`](https://docs.li.fi/api-reference/advanced/populate-a-step-with-transaction-data) exactly as it was returned, including `routeSlippage`. LI.FI re-quotes each step to build the transaction and then checks the transaction's bytes against the commitment:

* If the fresh quotes can still deliver the floor, the transaction encodes it and the response returns the same `toAmountMin`.
* If they can't, the request fails with `409` and `ROUTE_SLIPPAGE_FLOOR_UNREACHABLE`. Request a new quote and show the user the new floor. LI.FI never lowers the floor to make a build pass.

> [!WARNING]
>
> Don't edit `routeSlippage`, and don't change the step's recipient before you post it.
>
> * A commitment that doesn't match the step fails with `400` and `ROUTE_SLIPPAGE_COMMITMENT_INVALID`. Examples: a floor that isn't `ceil(referenceToAmount × (1 − slippage))`, or a different recipient or destination token.
> * A raised floor only makes the transaction stricter. The build fails with `409` if the fresh quote can't reach it.
> * A step posted without `routeSlippage` is built with per-step slippage instead.

***

## Track the outcome

For a route-protected transfer, [`/v1/status`](https://docs.li.fi/api-reference/check-the-status-of-a-cross-chain-transfer) adds two fields:

* `quote.routeSlippage` is the commitment the transaction was built with.
* `routeSlippage` is the verdict.

| `routeSlippage` field | Description |
| - | - |
| `outcome` | `PENDING` while the transfer is in flight. `FULFILLED` when the committed token arrived on the committed chain with at least the floor. `NOT_FULFILLED` otherwise. |
| `minimumToAmount` | The floor |
| `deliveredAmount` | What arrived in the committed token, once known |
| `shortfall` | Why the floor wasn't met. `REVERTED`: the source transaction reverted, so the user kept the input. `REFUNDED`: the bridge refunded. `DIFFERENT_TOKEN`: another token was delivered, for example a recovered bridged token. `BELOW_FLOOR`: the committed token arrived under the floor, which isn't an expected outcome (see [cross-chain settlement](#cross-chain-settlement)). |

```json theme={"system"}
{
  "status": "DONE",
  "routeSlippage": {
    "outcome": "FULFILLED",
    "minimumToAmount": "396000000000000000",
    "deliveredAmount": "398700000000000000"
  }
}
```

A refund, or the delivery of a different token, is never reported as `FULFILLED`.

***

## Errors

Route-wide slippage errors use the standard [error codes](https://docs.li.fi/api-reference/error-codes.md). The reason is the stable prefix of `message`, and of `reason` in `filteredOut`, so you can match on it without parsing the rest:

```json theme={"system"}
{
  "message": "ROUTE_SLIPPAGE_FLOOR_UNREACHABLE: <detail>",
  "code": 1007
}
```

| Reason | HTTP status | Code | When | What to do |
| - | - | - | - | - |
| `ROUTE_SLIPPAGE_UNAVAILABLE` | 422 | 1004 | Route-wide slippage is switched off | Do not silently retry in step scope. If switching modes, obtain a new quote, explain the changed protection, show the new minimum output, and require user confirmation before execution. |
| `ROUTE_SLIPPAGE_INVALID_SLIPPAGE` | 400 | 1011 | `slippage` isn't a decimal from `0` up to `1`, or has more than eight decimal places | Send a valid budget such as `0.01` |
| `ROUTE_SLIPPAGE_UNSUPPORTED` | listed in `filteredOut` | — | The route can't be protected (see [supported routes](#supported-routes)) | Choose a supported route. If switching to per-step slippage, obtain a new quote, explain the changed protection, show the new minimum output, and require user confirmation before execution. |
| `ROUTE_SLIPPAGE_FLOOR_UNREACHABLE` | 409 when building; listed in `filteredOut` when quoting | 1007 | No quote can deliver the floor | Request a new quote; the user approves the new floor |
| `ROUTE_SLIPPAGE_COMMITMENT_INVALID` | 400 | 1011 | The posted step's `routeSlippage` was changed or is malformed | Post the step exactly as it was returned |

***

## Next steps

* If [Smart Slippage](https://docs.li.fi/enterprise/smart-slippage.md) is enabled for your integrator, use its recommendations to choose a budget.
* Follow transfers to completion with [status tracking](https://docs.li.fi/introduction/user-flows-and-examples/status-tracking.md).


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