Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions api-reference/solve-rfq/overview.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
---
title: "Overview"
description: "Request a firm, liquidity-backed quote through LI.FI Intents, or route a swap against public on-chain liquidity"
---

Solve RFQ covers the two ways to get an executable price out of Sprinter.

| | **LI.FI Intents** | **Swap API** |
|---|---|---|
| **Liquidity** | Sprinter Liquidity — underwritten, reserved for your quote | Public on-chain AMM liquidity |
| **Price** | Firm. Reserved for 15s and filled at exactly that price | Indicative, subject to slippage |
| **Use for** | Redemptions, subscriptions, and any flow where the user is promised a price | Generic token swaps, and as a fallback when no Sprinter route exists |
| **Requires** | Asset onboarded and route configured with Sprinter | Nothing — any supported pair |
| **Base URL** | `https://api.sprinter.tech` | `https://swaps.sprinter.tech/{network}` |
| **Auth** | `X-Auth-Token` header | HTTP Basic |

<Tip>
**Default to LI.FI Intents.** It is the path that draws on Sprinter Liquidity, which is what makes an instant fill at a promised price possible. Reach for the Swap API when the pair is not onboarded with Sprinter, or as a fallback when a quote does not return.
</Tip>

## LI.FI Intents

Two calls. The first prices the order and holds the liquidity; the second turns that into a transaction the user signs.

```
GET /lifi-intents/rfq → quote (liquidity reserved, 15s)
POST /lifi-intents/transaction → same quote + unsigned open() calldata
→ user sends it; Sprinter fills
```

| Endpoint | Description |
|----------|-------------|
| [`GET /lifi-intents/rfq`](/api-reference/sprinter/lifi-intents/rfq) | Firm quote backed by reserved Sprinter liquidity |
| [`POST /lifi-intents/transaction`](/api-reference/sprinter/lifi-intents/transaction) | Escrow `open` transaction for a quote |

Authenticate the RFQ call with your API key in the `X-Auth-Token` header. The transaction call carries the quote itself, so it needs no header.

The responses follow LI.FI's own [intents API](https://docs.li.fi/lifi-intents/intents-api/request-quote) shapes, with one difference worth coding for: when Sprinter cannot serve a request it returns **`404`**, not a `200` with an empty `quotes` array.

<Note>
Quotes only return for assets Sprinter has onboarded — underwritten, allocated liquidity to, and configured routes for. See the [Asset Issuer quickstart](/quickstart/asset-issuer) for what onboarding involves.
</Note>

## Swap API

Route a swap against public on-chain liquidity and get back executable call data. No onboarding, no reservation, no firm price.

| Endpoint | Description |
|----------|-------------|
| [`GET /v1/route`](/api-reference/solve/get-v1route) | Optimal swap route and execution call data |

See the [Swap API overview](/api-reference/solve/overview) for base URLs, authentication, and the response format.

## Not this: the Liquidity API

[Sprinter Liquidity](/api-reference/sprinter/liquidity/overview) exposes the borrow-quote and signing endpoints directly. That surface is for **crosschain solvers** running their own fill infrastructure — it hands you a borrow authorization, not a transaction, and expects you to settle the intent yourself.

If you are an asset issuer, wallet, or application asking Sprinter for a price, use LI.FI Intents. The RFQ endpoint runs the same pricing and reservation pipeline and returns something you can sign.
156 changes: 156 additions & 0 deletions api-reference/sprinter/lifi-intents/rfq.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
---
title: "Request a LI.FI Intents Quote"
sidebarTitle: "RFQ"
openapi: get /lifi-intents/rfq
---

Prices an order against Sprinter liquidity and **reserves that liquidity** for the quote's validity window. The response is shaped like a single element of LI.FI's [request-quote](https://docs.li.fi/lifi-intents/intents-api/request-quote) response, so an existing LI.FI intents client can consume it unchanged.

<Warning>
A quote is valid for **15 seconds**. The reservation expires with it. Call [`POST /lifi-intents/transaction`](/api-reference/sprinter/lifi-intents/transaction) immediately — do not cache a quote or show it to a user for confirmation before converting it.
</Warning>

## Behaviour

| | |
|---|---|
| **Exclusivity** | Always exclusive to Sprinter. `metadata.exclusiveFor` returns the filler address |
| **Protocol** | Always `lifi-escrow`. Your API key must be provisioned for it |
| **Quotes returned** | Exactly one, or an error. There is no empty `quotes` array |
| **Amount basis** | `type=ExactInput` (default) reads `amount` as the input; `ExactOutput` reads it as the output |
| **Token defaults** | `srcToken` defaults to the same token symbol as `token`, resolved on the source chain |

## Errors

| Status | Meaning | Retry? |
|--------|---------|--------|
| `400` | No route configured for this chain/token pair, or invalid parameters | No — fix the request or ask about onboarding the route |
| `404` | Route exists, but no pool can serve it right now — capacity is reserved by other in-flight quotes, or pricing is unavailable | Yes, after a short backoff. Reservations expire in 15s, so pressure clears quickly |
| `401` | Missing or unrecognized `X-Auth-Token` | No |

<Tip>
`404` is a capacity answer, not a validity answer. Fall back to your own path if it persists, but a single retry a few seconds later often succeeds.
</Tip>

<RequestExample>
```bash cURL
curl --request GET \
--url 'https://api.sprinter.tech/lifi-intents/rfq?srcChain=eip155:8453&dstChain=eip155:42161&amount=100000000&token=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&user=0x1F98431c8aD98523631AE4a59f267346ea31F984' \
--header 'X-Auth-Token: YOUR_API_KEY'
```

```python Python
import requests

response = requests.get(
"https://api.sprinter.tech/lifi-intents/rfq",
params={
"srcChain": "eip155:8453",
"dstChain": "eip155:42161",
"amount": "100000000",
"token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
"user": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
},
headers={"X-Auth-Token": "YOUR_API_KEY"},
)
quote = response.json()["quotes"][0]
```

```javascript JavaScript
const params = new URLSearchParams({
srcChain: "eip155:8453",
dstChain: "eip155:42161",
amount: "100000000",
token: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
user: "0x1F98431c8aD98523631AE4a59f267346ea31F984",
});

const response = await fetch(
`https://api.sprinter.tech/lifi-intents/rfq?${params}`,
{ headers: { "X-Auth-Token": "YOUR_API_KEY" } }
);
const { quotes } = await response.json();
const quote = quotes[0];
```

```go Go
package main

import (
"fmt"
"io"
"net/http"
)

func main() {
url := "https://api.sprinter.tech/lifi-intents/rfq" +
"?srcChain=eip155:8453&dstChain=eip155:42161&amount=100000000" +
"&token=0xaf88d065e77c8cC2239327C5EDb3A432268e5831" +
"&user=0x1F98431c8aD98523631AE4a59f267346ea31F984"

req, _ := http.NewRequest("GET", url, nil)
req.Header.Set("X-Auth-Token", "YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
</RequestExample>

<ResponseExample>
```json 200
{
"quotes": [
{
"validUntil": 1754481615,
"eta": 45,
"quoteId": "3f8a1c72-95e4-4d6b-b0a1-2c7e9f4d8a13",
"provider": "sprinter",
"preview": {
"inputs": [
{
"user": "0x00010000022105141f98431c8ad98523631ae4a59f267346ea31f984",
"asset": "0x0001000002210514833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"amount": "100000000"
}
],
"outputs": [
{
"receiver": "0x0001000002a4b1141f98431c8ad98523631ae4a59f267346ea31f984",
"asset": "0x0001000002a4b114af88d065e77c8cc2239327c5edb3a432268e5831",
"amount": "99850000"
}
]
},
"metadata": {
"exclusiveFor": "0x4c4A2f8c81640e47606d3fd77B353E87Ba015584"
},
"failureHandling": "refund-automatic"
}
]
}
```

```json 404
{
"error": "no available pools for request"
}
```
</ResponseExample>

## Address format

`user`, `receiver` and `asset` inside `preview` are [ERC-7930](https://eips.ethereum.org/EIPS/eip-7930) interoperable addresses — chain id and account packed into one byte string:

```
version(2) | chainType(2) | chainRefLen(1) | chainRef(n) | addrLen(1) | addr(20)
```

`0x0001000002210514833589fc...2913` decodes to USDC on Base (`0x2105` = 8453).

`metadata.exclusiveFor` is a plain 20-byte hex address, not ERC-7930.

## Next step

Pass the quote object straight to [`POST /lifi-intents/transaction`](/api-reference/sprinter/lifi-intents/transaction) to get the transaction that opens the order.
140 changes: 140 additions & 0 deletions api-reference/sprinter/lifi-intents/transaction.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
---
title: "Build the Escrow Open Transaction"
sidebarTitle: "Transaction"
openapi: post /lifi-intents/transaction
---

Turns a quote into an unsigned `open` call on the LI.FI input settler escrow. POST the quote object you received from [`GET /lifi-intents/rfq`](/api-reference/sprinter/lifi-intents/rfq); you get the same object back with `transactionRequest` populated.

Sending that transaction escrows the inputs on the origin chain and broadcasts the order to solvers.

<Warning>
Include the `quoteId` from the RFQ response. Without it the endpoint still returns valid calldata, but **no liquidity is reserved** and the order is not guaranteed a Sprinter fill — you get no error saying so.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the /rfq endpoint ( quote) , we should mention here, visible the reservation time. It is 60 seconds. For an existing rfq quoteId the new reservation will be refreshed to 60 seconds from NOW when using in /transaction or directly 60 seconds from now if not used with a quoteId. Also keep in mind that this behavior is shared among pool with other solvers ... so if someone plays with /transaction for testing purpose it can reserve a lot of liquidity for 60 seconds

</Warning>

## What the endpoint fills in

You supply `preview`; everything else is derived:

| Field | Derived as |
|---|---|
| `nonce` | Generated per request |
| `fillDeadline` | ~6 minutes out — 1 minute exclusive to Sprinter, then 5 minutes open to any solver |
| `expires` | `fillDeadline` + a settlement buffer: ~13 minutes for same-chain orders, ~12 hours for cross-chain |
| `inputOracle` / output oracle | From Sprinter's configuration |
| Exclusivity | Encoded per output; defaults to Sprinter's filler address |
| `value` | Sum of native-token inputs, hex encoded. `0x0` for ERC-20-only orders |

You can override `nonce`, `expires`, `fillDeadline` and `inputOracle` by setting them in the `order` object on the request, and override the exclusive filler with `metadata.exclusiveFor`. Leave them unset unless you have a specific reason — the defaults are what the reservation is priced against.

## Constraints

- Every entry in `preview.inputs` must be on the same origin chain. Mixed origins are rejected with `400`.
- The returned transaction must be sent by `transactionRequest.from` — the payer named in the first input.
- Send it promptly. Converting the quote extends the reservation by one minute; after that the liquidity is released.

<RequestExample>
```bash cURL
curl --request POST \
--url 'https://api.sprinter.tech/lifi-intents/transaction' \
--header 'Content-Type: application/json' \
--data @quote.json
```

```python Python
import requests

# `quote` is quotes[0] from the RFQ response, passed through unchanged
response = requests.post(
"https://api.sprinter.tech/lifi-intents/transaction",
json=quote,
)
tx = response.json()["transactionRequest"]
```

```javascript JavaScript
// `quote` is quotes[0] from the RFQ response, passed through unchanged
const response = await fetch(
"https://api.sprinter.tech/lifi-intents/transaction",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(quote),
}
);
const { transactionRequest } = await response.json();

// send it from the user's wallet
const hash = await walletClient.sendTransaction({
to: transactionRequest.to,
data: transactionRequest.data,
value: BigInt(transactionRequest.value),
chainId: transactionRequest.chainId,
});
```

```go Go
package main

import (
"bytes"
"fmt"
"io"
"net/http"
)

func main() {
// quoteJSON is quotes[0] from the RFQ response, passed through unchanged
resp, _ := http.Post(
"https://api.sprinter.tech/lifi-intents/transaction",
"application/json",
bytes.NewReader(quoteJSON),
)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
```
</RequestExample>

<ResponseExample>
```json 200
{
"validUntil": 1754481615,
"eta": 45,
"quoteId": "3f8a1c72-95e4-4d6b-b0a1-2c7e9f4d8a13",
"provider": "sprinter",
"preview": {
"inputs": [
{
"user": "0x00010000022105141f98431c8ad98523631ae4a59f267346ea31f984",
"asset": "0x0001000002210514833589fcd6edb6e08f4c7c32d4f71b54bda02913",
"amount": "100000000"
}
],
"outputs": [
{
"receiver": "0x0001000002a4b1141f98431c8ad98523631ae4a59f267346ea31f984",
"asset": "0x0001000002a4b114af88d065e77c8cc2239327c5edb3a432268e5831",
"amount": "99850000"
}
]
},
"metadata": {
"exclusiveFor": "0x4c4A2f8c81640e47606d3fd77B353E87Ba015584"
},
"failureHandling": "refund-automatic",
"transactionRequest": {
"from": "0x1F98431c8aD98523631AE4a59f267346ea31F984",
"to": "0x6E9a1b3F0c5D2a8B4e7C1f9A3d6B0e5C8f2A4d71",
"chainId": 8453,
"data": "0xff2b0c2b0000000000000000000000000000000000000000000000000000000000000020",
"value": "0x0"
}
}
```
</ResponseExample>

## After the fill

Sprinter fills on the destination chain within the exclusivity window and is repaid when the escrow settles. If nobody fills before `fillDeadline`, the order is reclaimable on the escrow contract — `failureHandling` is `refund-automatic`, so nothing is stranded.
6 changes: 6 additions & 0 deletions api-reference/sprinter/liquidity/overview.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,12 @@ The Liquidity API exposes **Sprinter Intent Liquidity** that can be accessed thr
These are **zero-collateral** loans — solvers borrow without locking upfront capital. Repayment is secured by the intent protocol's escrow and settlement flow.
</Note>

<Warning>
**This is the solver-facing surface.** It returns a borrow authorization and expects you to run your own fill and settlement.

If you are an asset issuer, wallet, or application that wants a price and a transaction to send, use [Solve RFQ](/api-reference/solve-rfq/overview) instead. [`GET /lifi-intents/rfq`](/api-reference/sprinter/lifi-intents/rfq) runs this same pricing and reservation pipeline and hands back something you can sign.
</Warning>

## Base URL

```
Expand Down
Loading