# Request & Lifecycle Reference

:::caution
**Status: Beta (v0.1) — dark-launched.** The endpoints below are implemented and gated behind the `PredictionMarketsRFSEnabled` feature flag (employees-only first; they return `404` everywhere the flag is off). Field names are camelCase to match the rest of the Predictions REST surface. Wire shapes match the merged implementation (PREDICT-4876); enum encodings differ between REST and WebSocket — see the note under [`RFSSummary`](#rfssummary). These pages are intentionally not linked from the sidebar during the rollout.
:::

The RFS-specific surface is small: **two write entry points that share one implementation, one response object (`RFSSummary`), and a short request lifecycle.** Once the request resolves, everything else — orders, fills, positions, settlement — comes from the standard combo orderbook data model and lifecycle, identified by the derived `comboSymbol`.

## Two ways to issue an RFS

PREDICT-4876 merged combo creation and RFS issuance into one server-side path, while keeping **both REST entry points**. They differ only in how legs are identified; the validation, idempotency, audit, and broadcast semantics are identical.

| Endpoint | Use when | Legs identified by | Response |
|---|---|---|---|
| `POST /v1/prediction-markets/combos/rfs` | You want to issue an RFS directly. | `instrumentSymbol` + `direction` (resolved to contract ids server-side). | `201 Created` → [`CreateRFSResponse`](#createrfsresponse) |
| `POST /v1/prediction-markets/combos` with an `rfs` block | You're creating a combo and want to solicit liquidity on it in the same call. | `contractId` + `requiredOutcome`. | `200`/`201` → `CreateComboResponse` (carries `rfs`) |

Both are gated by `PredictionMarketsRFSEnabled` (a request carrying an `rfs` block returns `404` while the flag is dark, before the body is parsed, so a dark-feature payload can't leak its shape via validation errors). Both require the `PredictionsNewOrder` permission on a trading account.

Issuance is **idempotent per (combo, requester)**: resubmitting identical parameters returns the existing active RFS; submitting *different* parameters while one is still active fails with `409 Conflict`.

## `RFSParams`

The RFS parameter block. It is the body of the `rfs` field on `POST /v1/prediction-markets/combos`, and — composed with `legs` — the body of the dedicated `POST /v1/prediction-markets/combos/rfs` endpoint ([`CreateRFSRequest`](#createrfsrequest)). All fields are optional.

| Field | Type | Description |
|---|---|---|
| `side` | enum `Buy` \| `Sell` \| null | Direction the taker plans to trade the combo as a whole. Informational only — does not affect matching or the resulting (two-sided) orderbook. |
| `size` | integer \| null | Indicative size in **whole contracts**. Mutually exclusive with `notional` — supply at most one. Omitted means the taker declines to share size. |
| `notional` | string (decimal) \| null | Indicative **dollar amount**, string-decimal per the CDI convention (e.g. `"1000.00"`). Mutually exclusive with `size`. Mirrors `total_spend` on `PredictionsQuoteRequest`, so a taker who thinks in dollars need not convert to contracts first. |
| `structureTypes` | string[] | Taker-asserted discovery tags. Values: `SAME_EVENT`, `CROSS_EVENT`, `BASKET`, `SPREAD`, `CONDITIONAL`, `CROSS_CLASS`, `ANY`. Max 16 entries; each non-empty and ≤ 64 chars after trimming. |
| `eventId` | string \| null | Optional taker-asserted event id (SGP discovery aid), ≤ 128 chars. When absent, the server attempts to derive it from the legs when all legs share a single event. |

`assetClasses` is **server-derived and not accepted here** (see [`RFSSummary`](#rfssummary) for its current status).

### `RFSLeg`

The leg shape on the **dedicated** `POST /v1/prediction-markets/combos/rfs` endpoint, and the shape legs are returned in on `RFSSummary`. (On `POST /v1/prediction-markets/combos`, legs use the combo `contractId` shape instead — see below.)

| Field | Type | Description |
|---|---|---|
| `instrumentSymbol` | string | A currently listed single-contract ticker (e.g. `GEMI-BTC-EOY26-HI120000`). Combos cannot be legs. |
| `direction` | enum `Yes` \| `No` | Which side of the underlying contract. At v0.1 launch the same constraints as combo contracts apply (YES-only is the working assumption). |
| `ratio` | integer \| null | Quantity of this leg per combo contract. Defaults to `1` when omitted; an explicit `0` is rejected. Non-1 values are reserved for future structures (e.g. spreads). |

On `POST /v1/prediction-markets/combos`, each leg is instead `{ contractId, requiredOutcome, ratio? }` — `contractId` accepts a JSON string (`"373432"`) or whole number (`373432`), `requiredOutcome` is `"Yes"`/`"No"`. The server derives each broadcast `RFSLeg`'s `instrumentSymbol` and `direction` from the resolved leg contracts.

## `CreateRFSRequest`

Body for `POST /v1/prediction-markets/combos/rfs` — [`RFSParams`](#rfsparams) plus the legs.

| Field | Type | Cardinality | Description |
|---|---|---|---|
| `legs` | [`RFSLeg`](#rfsleg)`[]` | 2–6 | Ordered list of legs. Order is canonical and used to derive the `comboSymbol`. |
| *(plus all [`RFSParams`](#rfsparams) fields)* | | optional | `side`, `size` \| `notional`, `structureTypes`, `eventId`. |

## `CreateRFSResponse`

| Field | Type | Description |
|---|---|---|
| `request` | [`RFSSummary`](#rfssummary) | The persisted RFS. |

## `RFSSummary`

The persistent record of an RFS, mirroring the proto `RequestForStream`. Returned under `request` on the dedicated endpoint and under `rfs` on the combo-create response.

| Field | Type | Description |
|---|---|---|
| `rfsId` | string (UUID) | Globally unique. Stable across REST, WebSocket, and FIX (FIX `QuoteReqID`). |
| `comboSymbol` | string | The derived combo instrument ticker, content-addressable from the leg set per the [combo ticker format](/prediction-markets/combo-contracts/overview#ticker-format). Identical leg sets always produce the same `comboSymbol`; duplicate submissions reuse the existing book. **Clients should key off this string** to interact with the resulting orderbook on every surface. |
| `legs` | [`RFSLeg`](#rfsleg)`[]` | Ordered list of legs (with resolved `instrumentSymbol`/`direction`). |
| `side` | enum `Buy` \| `Sell` \| null | Echoes the request. Informational only. |
| `size` | integer \| null | Indicative size in whole contracts, if shared. Mutually exclusive with `notional`. |
| `notional` | string (decimal) \| null | Indicative dollar amount, if shared. Mutually exclusive with `size`. |
| `structureTypes` | string[] | Echoes the request's tags. |
| `assetClasses` | string[] | Derived from the legs' underlying contracts. **Currently always empty** — the leg→category derivation is pending the PREDICT-4732 categories rework. When populated it will drive the maker-side discovery filter. |
| `eventId` | string \| null | Populated when all legs share a single event (SGP discovery). Null otherwise. |
| `state` | enum `RfsState` | One of `RFS_STATE_REQUESTED`, `RFS_STATE_OPEN`, `RFS_STATE_CLOSED`, `RFS_STATE_REJECTED`. A successful create returns `RFS_STATE_OPEN`. See [Request lifecycle](#request-lifecycle). |
| `comboCreatedAt` | timestamp (RFC 3339, UTC) \| null | The underlying **combo orderbook** creation time — *not* the RFS row's own create time (server-internal row timestamps are not surfaced). |
| `comboContractId` | integer (int64) | Internal contract id for the underlying combo. A convenience field; clients should key off `comboSymbol`. |

:::note Enum encoding differs by surface
On **REST**, `side`/`direction`/`state` serialize as `Buy`/`Sell`, `Yes`/`No`, and `RFS_STATE_OPEN`. On the **`requestForStream` WebSocket broadcast**, the same values use the full proto enum names — `RFS_SIDE_BUY`, `RFS_LEG_DIRECTION_YES`, `RFS_STATE_OPEN` — and abbreviated JSON keys. Document each surface against its own page; see [WebSocket Reference](/prediction-markets/combos-rfs/websocket-streams).
:::

The request is **public and anonymous**: it is broadcast on the public `requestForStream` channel with no taker identifier on the wire, and the broadcast strips the internal-only fields (`comboContractId` and the server-internal row timestamps). The exchange knows the submitter (for clearing, risk, and self-trade prevention); the public stream does not.

There is **no `created` flag on `RFSSummary`.** Whether a brand-new combo book was opened or an existing one re-solicited is observable two ways: the combo-create endpoint returns `alreadyExisted` on its response, and `comboCreatedAt` reflects when the combo book was first created.

## Request lifecycle

There are **two lifecycles**, and keeping them separate is the key to understanding RFS:

1. **The RFS request** — a transient action: submit, validate, persist, announce. Resolves in well under a second, then it's done.
2. **The combo contract** — the long-lived instrument the request creates or references. Its lifecycle (trading, halts, settlement, delisting) is the **standard combo orderbook lifecycle**, documented in [Combo Contracts](/prediction-markets/combo-contracts/overview).

### Request states (`RfsState`)

| State | Meaning |
|---|---|
| `RFS_STATE_REQUESTED` | Submitted; the exchange is validating the legs and deriving the `comboSymbol`. Transient, pre-validation. |
| `RFS_STATE_OPEN` | Succeeded and announced on [`requestForStream`](/prediction-markets/combos-rfs/websocket-streams#requestforstream). The combo contract is live. The create endpoints return this state on success. |
| `RFS_STATE_CLOSED` | Natural sunset of an `OPEN` RFS — e.g. the underlying combo settled or was swept by housekeeping. Surfaced on the broadcast/replay feed, not by the create call. |
| `RFS_STATE_REJECTED` | Failed validation, auth, or risk before reaching `OPEN` (unknown/ineligible leg, malformed payload). No contract created or affected. |

```
   ┌──────────────────────┐
   │ RFS_STATE_REQUESTED  │
   └──────────┬───────────┘
              │ validate legs, derive comboSymbol, persist
              ▼
   ┌──────────────────────┐        ┌──────────────────────┐
   │   RFS_STATE_OPEN      │        │  RFS_STATE_REJECTED   │
   └──────────┬───────────┘   or   └──────────────────────┘
              │ combo settles / housekeeping     invalid request,
              ▼                                   nothing created
   ┌──────────────────────┐
   │  RFS_STATE_CLOSED     │
   └──────────────────────┘
```

The taker never has to manage anything past `OPEN`: once announced, the request's job is done and the combo contract takes over on its own lifecycle. `CLOSED` is bookkeeping on the RFS row that follows the combo's sunset — not an action the taker takes.

### The request is one-shot; quote lifetime is the maker's call

An RFS is **sent once**. It is not a standing session the taker maintains, and it does not impose any duration on the responses it draws. What happens next is entirely up to the makers:

- A maker decides whether to quote at all.
- A maker decides **how long their quotes stay live** — their orders rest on the combo book until they cancel them, exactly like any other order on the venue. There is no quote TTL imposed by RFS and no obligation to keep a quote up for any minimum time.
- A maker can re-price by cancelling and re-posting, add depth, or pull out entirely, on their own schedule.

So the request seeds demand; the makers own the supply side and its timing. A taker watching the book sees whatever quotes makers have chosen to leave live at that moment.

### Handoff to the combo contract

When the request is `RFS_STATE_OPEN`:

1. The exchange publishes a `RequestForStreamEvent` on the `requestForStream` channel with the derived `comboSymbol`.
2. The combo instrument is visible on standard market-data feeds: `{comboSymbol}@bookTicker`, `{comboSymbol}@depth*`, `{comboSymbol}@trade`, `contractStatus`.
3. Approved market makers post orders to `comboSymbol` via the standard order-entry path (REST, WS, FIX).
4. The taker (or anyone) takes liquidity via standard aggressive orders.
5. Positions go into the standard combo position ledger and settle per the combo's underlying-leg resolution rules.

There is no separate "stream session" object to manage after `OPEN`. The mental model: "this combo now exists and has fresh demand on it — treat it like any other combo."

## Combo contract lifecycle (standard, referenced)

Everything below belongs to the combo contract, not RFS. It is identical whether the combo was pre-listed or created by an RFS, and is documented in [Combo Contracts](/prediction-markets/combo-contracts/overview).

### Halts

Halts apply to the combo's **underlying legs**. If a leg halts, the combo book's tradability is affected per standard combo halt behavior.

| Halt class | Effect on the combo |
|---|---|
| Routine event-window halt on a leg | Combo enters pre-settlement; standard combo halt behavior. |
| Data-integrity halt on a leg | Combo trading paused; standard combo halt behavior. |
| Market-wide halt | All books, combos included, are halted. |
| Voided leg | Combo voids per standard combo void handling; positions unwound at entry price (see [Combo Contracts](/prediction-markets/combo-contracts/overview)). |
| Sports event-state halt (pause, video review) | Applies to combos referencing the event; standard sports-leg halt. |
| Macro pre-release blackout | Combos referencing the affected print are blocked from new orders; standard macro halt. |

### Idle housekeeping

A combo that attracts no resting interest for an extended idle window may be delisted by housekeeping — most relevant for combos spun up on demand that never gain traction. Pre-listed combos may be retained by curation policy. Either way the behavior is part of the contract lifecycle; an RFS submission does not set or change a combo's delisting policy. Windows are operationally configured per asset class and are intentionally long; the duration is an [open question](/prediction-markets/combos-rfs/overview#open-questions).

## What's *not* in the RFS surface

These apply to the combo once it's live but are standard combo orderbook concerns, identified by `comboSymbol`:

| Concern | Source |
|---|---|
| Orders on the combo book | Standard order data model + lifecycle (NEW / OPEN / PARTIALLY_FILLED / FILLED / CANCELLED) |
| Trade records, fills, sweeps | Standard trade-tape and account-event data model |
| Positions, balances | Standard positions/balances data model |
| Per-leg execution allocation, self-trade prevention, fill-ratio SLAs | Venue-wide / combo orderbook |
| Settlement records | Standard combo settlement ([Combo Contracts](/prediction-markets/combo-contracts/overview#settlement)) |

The standard trade-record schema picks up no new fields on account of RFS. A combo behaves identically whether it was pre-listed or created by an RFS, so the `CMB` ticker prefix on `comboSymbol` is the signal that an instrument is a combo — no separate "how did this originate?" field is needed.

If you find yourself looking for an RFS-specific `Quote`, `Acceptance`, `Fill`, or `OrderState` object: you don't need one. Those are standard orderbook objects on `comboSymbol`.
