Request for quote (RFQ)

Ask permissioned responders to price size you do not want to show the book, then cross against the best quote through the same matching engine every other fill goes through.

An RFQ is a way to trade size without posting it. You state what you want — market, outcome, side, quantity — and registered responders quote you a price inside a short window. Accepting a quote executes a cross: a peer-to-peer fill at the agreed price, settled by the matching engine under the same locks, the same compliance gate, and the same price-state rules as an order-book trade.

The RFQ facility is off by default in every environment. If POST /rfq returns 503 RFQ_DISABLED, it is not enabled for the environment you are calling.

The lifecycle

1

Open a request

POST /rfq with market_id, outcome_id, side and quantity. The side is the side you want — responders take the other one. Opening a request reserves no funds.

2

Responders quote

Registered responders poll GET /rfq/open and answer with POST /rfq/{id}/quotes. Each responder has at most one live quote; re-quoting replaces it in place.

3

You accept

POST /rfq/{id}/accept. Omit quote_id to take the best price for your side — lowest when you are buying, highest when you are selling. Ties go to whoever quoted first.

4

The cross prints

The trade settles peer-to-peer and appears on the public trade tape like any other fill. Your rfq moves to EXECUTED and carries executed_trade_id.

State machine — every other transition returns 409:

FromTo
OPENQUOTED, EXPIRED, CANCELLED
QUOTEDQUOTED, ACCEPTED, EXPIRED, CANCELLED
ACCEPTEDEXECUTED
EXECUTED, EXPIRED, CANCELLED(terminal)

ACCEPTED is an in-flight state inside the accept transaction, not something you can observe and then act on: an accept either commits through to EXECUTED or rolls back to QUOTED.

The quote window

A window is 30 seconds by default and can be set between 5 and 120 seconds with window_seconds. When it elapses, the request expires and its live quotes expire with it.

Expiry is enforced two ways, which matters if you are building a bot: a background sweep closes elapsed windows, and accept independently refuses a window that has already passed. You cannot accept a stale quote by racing the sweeper.

Quotes are firm at accept — not guaranteed

This is the most important thing to understand before you build against RFQ.

An RFQ locks nothing. No balance is reserved for you while the window is open, and no inventory is reserved by the responder. What you get instead is re-validation at execution: when you accept, the engine re-checks, under lock,

  • the agreed price against the current market mark (the band, below),
  • the market’s halt state and close time,
  • that the seller actually holds the shares,
  • that the buyer actually has the balance,
  • and your full compliance gate, against the agreed notional.

So a quote you accept can fail — with a typed error saying which check refused. That is a deliberate trade-off. Reserving funds for the length of every window would let anyone freeze a balance, or a market maker’s inventory, by opening requests they never intend to accept.

We do not describe RFQ pricing as guaranteed anywhere, and you should not build on the assumption that an accept always succeeds. Handle the refusal.

The price band

A cross must print within 500 basis points of the market mark — read as basis points of the probability scale, so |price − mark| ≤ 0.05 in absolute terms. A quote outside the band is refused when it is submitted, and again when it is accepted.

The mark is not the raw last-traded price. It is the same manipulation-resistant mark the platform uses for trigger orders — recent volume-weighted trades, then the top-of-book midpoint, then the last trade — computed while excluding both counterparties. Neither side of a cross can influence the number their own cross is measured against, so two accounts cannot wash-trade a price into existence and then cross at it.

If the market moves during your window, the band moves with it. A quote that was inside the band when it was given can be outside it by the time you accept.

Fees

Standard maker/taker fees at your tier apply. The requester pays taker (you asked for immediacy) and the responder pays maker (they supplied the price) — the same split a resting order and an incoming order get on the book.

The notional is quantized to cents exactly once, so buyer_debit == seller_credit + total_fee to the cent. See Fees.

Who can respond

Responding is permissioned, and it takes both of:

  1. the rfq:respond scope on your API key (a write scope — it requires explicit acknowledgement at key creation, because a quote is a binding offer someone can cross against), and
  2. membership in the responder registry, which is administered by Drazill.

Holding the scope alone does not let you quote. Both checks fail closed, so an empty registry means nobody may respond.

Responders are API actors in v1. There is no responder interface in the Drazill web app, and there is no broadcast channel for new requests — poll GET /rfq/open roughly every 12 seconds while you want to be quoting.

What each side can see

Neither side learns who the other is before the trade prints.

Requester seesResponder sees
Market, outcome, side, size✅✅
Window expiry✅✅
Quote prices✅ all live quotes✅ their own only
Responder identity❌—
Requester identity—❌
Other responders’ quotes—❌

GET /rfq/open has no requester_id field, and quote objects have no responder_id field. The window is a sealed auction: a responder cannot see what anyone else quoted and undercut it by a tick.

Realtime updates

Subscribe to the rfq:{rfq_id} WebSocket channel for rfq_update events on every lifecycle transition. The channel is participants-only — the requester and registered responders — and every other subscriber is refused.

Payloads carry the status, the request terms, and the current best quote price. They never carry a responder identity, because the channel is shared by every participant.

Treat the channel as a nudge, not as the source of truth: re-read GET /rfq/{id} when an event arrives.

Test keys

A drzl_test_ sandbox key (or any key holding env:paper) is blocked from RFQ, exactly as it is blocked from real-money orders. RFQ moves real money; the sandbox does not have an RFQ equivalent. See Paper vs live.

Limits

EndpointRate limit
POST /rfq10 / minute
POST /rfq/{id}/quotes60 / minute
POST /rfq/{id}/accept30 / minute
GET /rfq/open120 / minute

Requests are single-leg: one market, one outcome, one size. Quotes must cover the full requested quantity — partial quotes are refused. Multi-leg (combination) RFQ is not available.

POST /rfq and POST /rfq/{id}/accept accept an Idempotency-Key; a retried accept returns the same trade rather than printing a second one. See Idempotency.