# Maker Integration

## Who can quote

The discovery feed is public — no authentication is required to subscribe. Private deliveries require `view_orders`; submitting and confirming quotes require `place_orders`; withdrawing a quote requires `cancel_orders`. Quoting is **all-WebSocket**: one authenticated connection can carry discovery, private deliveries, and the quote actions.

## The complete maker workflow

```
1. Connect to wss://ws.gemini.com (authenticated)
2. Subscribe to requestForQuote          (public discovery feed)
3. Subscribe to requestForQuote@account  (your private deliveries)

   ── for each requestForQuote event with S == "OPEN" that matches your interest ──

4. Price the combo from its legs (l: contract id + YES/NO outcome per leg)
5. rfq.submit_quote { rfqId, price, quantity }   within the 1-second quoting window
                                                      → keep the returned quoteId
6. (optional) rfq.withdraw_quote if you need to pull the quote before the window closes

   ── if your quote wins and the requester accepts ──

7. An ACCEPTED delivery arrives on requestForQuote@account carrying your quoteId
8. rfq.confirm_quote { rfqId, quoteId, confirm: true }   within 1 second
9. Watch the same stream for FINALIZED (fill quantity arrives on the public feed's f)
```

Pseudocode:

```js
ws.subscribe(["requestForQuote", "requestForQuote@account"])

const myQuotes = new Map() // rfqId -> quoteId

ws.onMessage(async (msg) => {
  if (msg.e !== "requestForQuote") return

  // Public discovery: a new auction to consider.
  if (msg.S === "OPEN" && msg.l && !myQuotes.has(msg.r)) {
    if (!interestedIn(msg.l)) return
    const price = priceCombo(msg.l)                    // your pricing model
    const quantity = sizeFor(msg.n ?? msg.q, price)    // notional XOR quantity sizing
    const quote = {
      rfqId: msg.r,
      price: price.toFixed(2),
      quantity: String(quantity),
    }
    // Omit validUntil to use w, the service-assigned default.
    const res = await ws.call("rfq.submit_quote", quote)
    myQuotes.set(msg.r, res.quoteId)
    return
  }

  // Private delivery: the requester accepted this account's quote.
  if (msg.x === "ACCEPTED" && msg.q === myQuotes.get(msg.r)) {
    const ok = await lastLook(msg)                     // re-check your price/risk
    await ws.call("rfq.confirm_quote", {
      rfqId: msg.r,
      quoteId: msg.q,
      confirm: ok,
    })
  }
})
```

## Quoting rules

- **One immutable quote per account per RFQ.** No revisions; withdrawing does not free the slot. Price it right the first time.
- **Price is per contract, strictly between 0 and 1**, on the venue price tick. The default tick is `0.001`.
- **Quantity is your maximum and must cover the complete request to be eligible.** A notional-sized RFQ requires `quantity × price >= notional`; a quantity-sized RFQ requires `quantity >= requestedQuantity`. The execution size is `floor(notional ÷ price)` contracts for a notional-sized RFQ or `floor(requestedQuantity)` for a quantity-sized one.
- **`validUntil` is a close-time eligibility deadline.** If supplied, it must be in the future. A quote remains eligible when `validUntil >= w`; a value before `w` expires the quote at close. If omitted, the service sets it to `w`. It is not checked again after winner selection.
- **The auction is side-effect-free while quoting.** Submission performs a collateral preflight but does not reserve funds. At close, the venue freshly checks candidates in price-time order and falls through to the next quote when a maker lacks available collateral. The requester and maker collateral holds are placed later, at requester acceptance and maker confirmation respectively.

## Execution and partial-fill risk

:::caution
The RFQ's full-size guarantee applies to the requester's fill-or-kill order, **not** to the selected maker's post-only order.

After the maker confirms, the venue places the maker's order first. Other market participants can trade against it before the requester's order arrives, so the maker can receive a partial fill. If the requester order then fails, the venue cancels only the maker order's unfilled remainder; completed maker fills remain and may create or change a position even though the RFQ ends in `FAILED`.

The reverse is also possible: the requester can fill in full against other resting liquidity, allowing the RFQ to reach `FINALIZED` while the selected maker fills only part, or none, of the quoted quantity. Monitor the standard authenticated order, position, and balance streams for actual maker fills rather than inferring them from the RFQ state or `f`.
:::

## Timing

The lifecycle uses a 1-second quoting window, a 5-second requester decision window, and a 1-second maker confirmation window. The authenticated acceptance does not carry a separate confirmation-deadline field. The confirmation window starts when the RFQ enters `CONFIRMING`, so respond immediately when the `ACCEPTED` delivery arrives.

| Phase | Timing |
|---|---|
| Maker quoting | Submit or withdraw strictly before `w` on the public `requestForQuote` broadcast. `w` is 1 second after RFQ creation. |
| Your quote's validity | Quote-scoped private deliveries mirror `validUntil` in `vu`. The quote is eligible when `validUntil >= w`; omitting it assigns `w`. The field has no effect after selection. |
| Requester decision | The requester has 5 seconds from the `PENDING_ACCEPTANCE` transition to accept the selected quote. |
| Maker confirmation | Confirm or decline strictly before the 1-second deadline measured from the `CONFIRMING` transition; otherwise the RFQ fails. |
| Hard auction expiry | `x` (number) on the public broadcast is the absolute upper bound and can shorten either post-close window. |

Authenticated lifecycle deliveries include a durable event ID in `i`. Treat `i` as an idempotency key because an at-least-once delivery can repeat the same transition.

## Anonymity

- The requester receives no quote submission or withdrawal events while the auction is open. After close, only the selected quote is disclosed; losing quotes remain undisclosed.
- Your identity is never revealed to the requester — quotes are keyed by `quoteId` only.
- The public feed never carries quote contents; losing quotes are never disclosed.

## What's not covered here

- **Opening RFQs** (the requester side) is a retail-facing flow in the Gemini app and website; there is no public API for creating RFQs.
- **Settlement mechanics** of the resulting combo position follow the standard combo contract lifecycle — see [Combo Contracts](/prediction-markets/combo-contracts/overview).

## See also

- [Quote Methods](/prediction-markets/combos-rfq/quote-methods) — full parameter and error tables.
- [WebSocket Streams](/prediction-markets/combos-rfq/websocket-streams) — message shapes.
- [Examples](/prediction-markets/combos-rfq/examples) — a worked auction end to end.
