Skip to content

RFQ

Three endpoints: ask for a quote, report the transaction you sent, read the status.

Request a quote

POST /v2/rfq/request

Requires trades:write. This request blocks while makers answer — up to the corridor's reply budget (750 ms by default). Set your client timeout well above it.

Send exactly one of sellAmount (exact-input) or buyAmount (exact-output):

json
{
  "chainId": 56,
  "sellToken": "0xCNGN",
  "buyToken": "0xUSDT",
  "sellAmount": "1000000000",
  "taker": "0xYourWallet"
}
FieldNotes
chainIdMust match your key's environment.
sellToken / buyTokenMust differ, and must form an enabled RFQ corridor.
sellAmountExact-input: the gross, fee-inclusive spend cap. Makers may debit less.
buyAmountExact-output: the exact amount you want to receive.
takerThe wallet that will sign and broadcast. The quote is bound to it.
preferredLiquidityWalletsOptional. Solicit these makers first; fall back to the rest. Max 10.
restrictedLiquidityWalletsOptional. Solicit only these makers, no fallback. Max 10.

preferredLiquidityWallets and restrictedLiquidityWallets are mutually exclusive — sending both is a 400. They work the same way as on v1.

A firm quote

json
{
  "data": {
    "rfqId": "rfq_…",
    "status": "quoted",
    "quote": {
      "sellAmount": "1000000000",
      "buyAmount": "612000000",
      "feeAmount": "99990",
      "takerPays": "999900010",
      "rateRay": "612000000000000000000000000",
      "expiresAt": "2026-08-07T12:00:04.500Z",
      "orderDeadline": "2026-08-07T12:00:34.000Z",
      "reactor": "0xReactor",
      "taker": "0xYourWallet",
      "encodedOrder": "0x…",
      "signature": "0x…"
    },
    "transactions": {
      "approval": { "to": "0xCNGN", "data": "0x095ea7b3…", "value": "0", "chainId": 56 },
      "swap":     { "to": "0xReactor", "data": "0x…", "value": "0", "chainId": 56 }
    },
    "routing": {
      "preferenceApplied": false,
      "restrictionApplied": false,
      "fallbackUsed": false,
      "targetMakerWallets": [],
      "preferredQuotesReceived": 0,
      "openMarketQuotesReceived": 2
    }
  }
}

The two numbers that matter for execution:

  • takerPays is what actually leaves your wallet — the maker's order output plus the protocol fee. On exact-input this is at most sellAmount; a maker that prices better than your cap debits less. Approve takerPays, not sellAmount.
  • buyAmount is what you receive.

expiresAt is the accept cutoff — the maker's own, which may be sooner than the corridor's TTL cap. orderDeadline is when the signed order stops being executable on chain; it is always at or after expiresAt. Treat expiresAt as your deadline.

The quote is returned exactly once

encodedOrder and signature come back from this call and nowhere else. GET /v2/rfq/{id} never replays them. Store them if you need them.

Executing it

  1. Approve takerPays of the sell token to quote.reactor. Skip it if the standing allowance already covers it — the TTL is seconds, don't spend it on a redundant approval.
  2. Send transactions.swap from the taker wallet. value is always "0"; the fee is taken in the sell token, not native.
  3. Re-check the clock before broadcasting. If a slow approval pushed you past expiresAt, request a new quote instead of sending — a late broadcast can only revert.

No quote

A no-quote is a 200, not an error. It means nobody would price your trade, which is a normal outcome.

json
{
  "data": {
    "rfqId": "rfq_…",
    "status": "no_quote",
    "reason": "no_valid_quote",
    "routing": { "…": "…" }
  }
}
reasonWhat happened
no_makers_onlineNo eligible maker was connected for this corridor.
no_restricted_liquidityYou restricted to specific wallets and none of them could quote.
no_valid_quoteMakers were asked but none returned a usable quote in time.

Retrying immediately is reasonable for no_valid_quote — makers reprice constantly. Retrying no_makers_online in a tight loop is not; back off.

Report the transaction

POST /v2/rfq/{id}/submit

Requires trades:write. Tell us the hash you broadcast so we can track it.

json
{ "txHash": "0xabc…" }

Idempotent on the same hash — resubmitting returns the current status. A different hash for an RFQ already reported is a 409.

Reporting is a courtesy, not a requirement: settlement is reconciled from the filled order on chain, so a fill is detected whether or not you call this. If the call fails transiently, retry it — but don't treat the failure as a failed swap.

Reporting after the fill is fine

If you wait for the receipt before reporting, reconciliation may already have marked the RFQ filled. Submitting the same hash then still returns 200 with the current status. That's the happy path finishing out of order, not a conflict.

Read the status

GET /v2/rfq/{id}

Requires trades:read. Scoped to the caller that created it — another partner's key gets 404, and so does a test key reading a live RFQ.

json
{
  "data": {
    "rfqId": "rfq_…",
    "status": "filled",
    "chainId": 56,
    "sellToken": "0xCNGN",
    "buyToken": "0xUSDT",
    "sellAmount": "999900010",
    "buyAmount": "612000000",
    "feeAmount": "99990",
    "taker": "0xYourWallet",
    "expiresAt": "2026-08-07T12:00:04.500Z",
    "txHash": "0xabc…",
    "filledAt": "2026-08-07T12:00:09.000Z",
    "failReason": null,
    "routing": { "…": "…" },
    "createdAt": "2026-08-07T12:00:00.000Z"
  }
}
statusMeaning
solicitingMakers are being asked. You won't normally see this.
quotedA firm quote was returned and is live.
no_quoteNobody quoted. Terminal.
submittedYou reported a transaction; we're waiting for it to settle.
filledSettled on chain. Terminal.
failedThe signed order's deadline passed without the transaction landing.
expiredThe quote lapsed without being executed. Terminal.

Once a winner exists, sellAmount and buyAmount are the settled amounts — the actual debit (including fee) and the amount delivered — not the cap you asked for. Before that they echo the request.

failed and expired aren't always final

A fill that indexes late corrects the record for up to 24 hours, so an RFQ can move from failed to filled. If you broadcast a transaction and it settled, trust the chain and keep polling rather than treating the first terminal verdict as the end.

Prefer webhooks over polling for settlement.