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

# RFQ

> Request, quote and accept block trades with sdk.rfq

`sdk.rfq` covers [RFQ (block trades)](https://docs.0xmonaco.com/developers/rfq.md): a taker requests quotes for a size, makers quote privately, the taker accepts one side of the aggregated view, and the selected makers get a 250 ms last look. Every call is signed. Every write takes an optional `idempotencyKey` (1–64 letters, digits or `. _ : -`); the SDK generates one when you omit it, and a retry with the same key and body returns the first result.

RFQ is available only to accounts it is enabled for; other calls fail with `RFQ_DISABLED`.

```typescript theme={null}
import { MonacoSDK, waitForRfqView } from "@0xmonaco/core";

// Initialize SDK with wallet client (see Initialization guide)

// Limits, timings, your creation quota and the markets RFQ is offered on
const caps = await sdk.rfq.getCapabilities();

// ── As taker ────────────────────────────────────────────────────────────────

// Request quotes for up to `quantity`. Omit `side` for a two-way RFQ, whose
// direction stays hidden from makers.
const rfq = await sdk.rfq.createRfq({
  tradingPairId,          // UUID
  product: "PERPETUAL",   // "SPOT" | "PERPETUAL"
  quantity: "5",          // maximum base quantity, not a guaranteed fill
  side: "BUY",            // optional: "BUY" | "SELL"
  leverage: "2",          // perpetuals only (required there, refused on spot)
  marginMode: "ISOLATED", // perpetuals only: "ISOLATED" (default) | "CROSS"
});

const rfqId = rfq.terms!.rfqId;

// Wait for a view you would accept, from the RfqView pushes (see below)
const { view } = await waitForRfqView(
  sdk,
  rfqId,
  (view) => view.ask?.availableQuantity === "5",
  { initialView: rfq.view, timeoutMs: 10000 },
);

// Accept one side of that view (binding: last look follows)
await sdk.rfq.acceptRfq(rfqId, { side: "BUY", viewVersion: view.viewVersion });

// The authoritative status: your current view and, once it ends, the outcome
const status = await sdk.rfq.getRfq(rfqId);

// Or cancel before accepting
await sdk.rfq.cancelRfq(rfqId);

// Your RFQs as the taker, newest first, every state included
const page = await sdk.rfq.listRfqs({ pageSize: 50 });
// follow page.nextPageToken until it is empty

// ── As maker ────────────────────────────────────────────────────────────────

// Place or replace your quote: a bid, an ask or both. Name your current
// version: 0 for your first quote, then `ownLatestVersion` from getRfq.
await sdk.rfq.upsertQuote(rfqId, {
  expectedVersion: 0,
  ask: { price: "64010.5", capacity: "5" },
  validityMs: 30000,      // 15000–60000, default 60000, capped at the request deadline
  leverage: "2",          // perpetuals only
});

// Withdraw your current quote
await sdk.rfq.withdrawQuote(rfqId, { expectedVersion: 1 });

// Selected: reject, or improve your frozen terms (a strictly better price or a
// higher capacity, never lower). An answer counts if it arrives by the
// deadline; later ones fail with RFQ_DEADLINE_PASSED. Silence executes.
await sdk.rfq.respondLastLook(rfqId, {
  acceptanceId,           // ownSelection.acceptanceId, or the RfqSelected push
  response: "IMPROVE",    // "REJECT" | "IMPROVE"
  improvedTerms: { price: "64009", capacity: "5" },
});
```

## Following the view

Follow the taker's view on the `RfqView` pushes rather than polling `getRfq`. `waitForRfqView(sdk, rfqId, predicate, options?)` does that, matching `rfqId` in any letter case: it resolves with the first view that satisfies `predicate`, keeping the push with the highest `sequence`, and calls `getRfq` only where a push may have been missed:

* once at the start, since pushes sent before it subscribed (makers can quote before `createRfq` returns) are not replayed;
* once after every reconnect;
* repeatedly, from 250 ms backing off to 2 s, while the socket is down or the `rfq` subscription is not confirmed;
* once when `timeoutMs` passes.

| Option | Meaning |
| - | - |
| `timeoutMs` | How long to wait, default 10000 |
| `initialView` | A view you already hold, such as `createRfq`'s `view`, checked first |
| `signal` | An `AbortSignal`; aborting rejects with its reason |

It resolves with `{ view, source }`, where `source` is `"push"`, `"read"` or `"initial"`. It rejects with an `InvalidStateError` when no view matched by the timeout or when an `RfqOutcome` push says the RFQ ended. When quotes change quickly, views in between may be skipped, but the last view pushed always reflects the latest change, so a predicate on the latest state is always seen.

## Pushes

New RFQs and last-look prompts arrive only on the authenticated `rfq` WebSocket channel; there is no call that lists open RFQs to quote. Narrow on `eventType`:

```typescript theme={null}
const unsubscribe = sdk.ws.rfq((event) => {
  switch (event.eventType) {
    case "RfqRequested": // a new RFQ you can quote (never your own wallet family's)
    case "RfqView":      // as taker: your view moved; keep the highest `view.sequence`
    case "RfqSelected":  // as maker: your last-look prompt, with your frozen fees and estimated net
    case "RfqOutcome":   // how an RFQ you are party to ended
  }
});

// Public, anonymised RFQ prints for one pair. Deduplicate on data.executionId.
// The optional third argument receives the subscribe-time snapshot: the
// pair's newest 50 RFQ prints, newest first.
sdk.ws.rfqTrades(
  tradingPairId,
  (event) => { /* event.data.price, event.data.quantity */ },
  (snapshot) => { /* RfqTradeEventData[] */ },
);

// A print can be missed. Recover the gap after the last one you saw:
// RFQ fills newer than it, oldest first. These are TradeEvents, keyed by
// data.tradeId, which equals a live print's data.executionId: deduplicate on
// that. complete is false when the page budget ran out before recovery finished.
const { trades, complete } = await sdk.trades.recoverRfqTrades(tradingPairId, {
  sinceExecutionId: lastSeenExecutionId,
});

// Or page the public RFQ history directly (recent fills; older RFQ fills stay
// on the unfiltered pages, tagged executionSource "RFQ")
await sdk.trades.getTrades(tradingPairId, { executionSource: "RFQ", pageSize: 100 });
```

A push is a notification, not authority: if you miss one, read the RFQ with `getRfq`. Keep the `rfq` connection open while you have quotes resting: see [Disconnects](https://docs.0xmonaco.com/developers/rfq.md#disconnects).


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