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

# Seller RFP Lifecycle

> Create, compose, inspect, revise, and evaluate seller RFP responses through the authenticated v3 MCP API.

The v3 seller RFP workflow turns one request into an immutable, auditable
request-and-response turn. It works through `/mcp/v3`; no Interchange page is
required. The authenticated account must be a Seller Account.

An **RFP** is the seller's durable commercial opportunity or exercise. An
**RFP turn** is one immutable request and its response. The response is the
seller proposal artifact inside that turn; clients do not save a second
seller-side Proposal object.

## Before creating an RFP

Response composition uses the Seller Account's own active Storefront products.
At least one matching product must have all of these catalog facts:

* a name and at least one priced option declaring a `guaranteed` or
  `non_guaranteed` delivery type;
* at least one channel, including a requested channel when the RFP specifies one;
* an explicit positive fixed or floor price with pricing-option ID, pricing
  model, ISO currency, and a compatible delivery type; and
* at least one canonical URL-free creative format (`format_kind` plus `params`).

Missing facts are never replaced with an empty offer. The turn finishes as
`needs_clarification` and identifies the missing catalog facts instead.
Composition scans at most 1,000 products and applies declared field,
collection, and artifact-size limits. A larger catalog or response also ends
in explicit clarification; Interchange never returns a partial proposal as
`ready`.

`request.budget` is an exact currency amount, not an arbitrary decimal. Its
`currency` must match the uppercase ISO 4217 contract published by the
`save_rfp` input schema and be supported by its runtime refinement; `amount`
must remain between the schema's published minimum and maximum. Decimal precision follows that currency's minor unit: for
example USD accepts two decimal places, BHD accepts three, and JPY accepts none.
An amount that cannot be represented in whole minor units is rejected before
the RFP is saved. Zero is accepted so an incomplete brief can be recorded, but
composition returns actionable `needs_clarification`; it never consumes worker
retries or emits zero-value allocation lines.

## Create and poll a turn

Call `save_rfp` with `action: "create"`. `clientRequestId` is the idempotency
key: retrying the exact request returns the same RFP and turn, while reusing the
key with a changed payload returns an idempotency conflict.

```json theme={null}
{
  "action": "create",
  "clientRequestId": "rfp:autumn-video:v1",
  "origin": {
    "kind": "manual",
    "buyer": "Northwind Media",
    "advertiser": "Contoso",
    "channels": ["video"]
  },
  "purpose": "draft",
  "request": {
    "brief": "Reach high-intent retail audiences with premium video.",
    "budget": { "amount": 125000, "currency": "USD" },
    "constraints": { "mustInclude": ["brand_safe"] }
  },
  "strategy": { "posture": "hold_value" }
}
```

The save returns immediately with `rfpId`, `turnId`, `responseState`, and a
safe `get` call. Poll the exact turn:

```json theme={null}
{ "kind": "rfp_turn", "id": "TURN_ID" }
```

The observable lifecycle is:

`queued → processing → ready | passed | needs_clarification | failed`

* `ready` contains a non-empty semantic response with bound products, prices,
  allocations, canonical formats, narrative sections, and composition receipts.
* `passed` is a deliberate, evaluated decision not to propose, not an absent
  response. An explicit `strategy.posture: "pass"` records the canonical truth
  gate and decision provenance on the turn.
* `needs_clarification` contains questions and the facts that could not be
  resolved. Correct the product catalog or request, then append a new turn.
* `failed` contains a stable reason and safe retry guidance. Do not treat it as
  a successful response or keep polling it as though it were processing.

Each composition attempt is lease- and generation-fenced. A reclaimed or late
worker cannot overwrite the artifact committed by the current attempt.

## Continue the lifecycle

`save_rfp` uses one closed action union. Fields from another action are
rejected rather than ignored.

| Action                   | Required values                                            | Effect                                                          |
| ------------------------ | ---------------------------------------------------------- | --------------------------------------------------------------- |
| `create`                 | `clientRequestId`, `origin`, `purpose`, `request`          | Creates the RFP and first turn, then queues composition         |
| `append_turn`            | `rfpId`, `parentTurnId`, `clientRequestId`, `request`      | Adds an immutable revision turn and queues composition          |
| `record_feedback`        | `rfpId`, `turnId`, `clientRequestId`, non-empty `feedback` | Appends bounded feedback without rewriting the response         |
| `request_representation` | `rfpId`, `turnId`, `clientRequestId`, `representation`     | Requests an idempotent representation of the immutable response |
| `release_turn`           | `rfpId`, `turnId`, `clientRequestId`                       | Marks an eligible live response for delivery                    |
| `record_outcome`         | `rfpId`, `turnId`, `clientRequestId`, non-empty `outcome`  | Appends the observed commercial result and finality             |

HTML, PDF, and PPTX renderers are not certified in this lifecycle slice.
Requests for those formats return
`representation_format_not_certified`; they do not fabricate a file or mutate
the response artifact.

## Search and audit

Use `search({"kind":"rfp"})` for opportunities and
`search({"kind":"rfp_turn"})` for immutable turns. RFP filters include state,
origin, purpose, buyer, advertiser, category, market, channel, posture,
Playbook version, cache mode, and outcome. Turn filters add RFP ID, response and
evaluation state, recipe/model/judge version, and feedback status.

Use `get({"kind":"rfp","id":"RFP_ID"})` for the opportunity summary,
latest and released turns, bounded turn history, outcome summary, and links.
Use `get({"kind":"rfp_turn","id":"TURN_ID"})` for the exact request,
response, receipts, diagnostics, feedback, outcome, and representation status.

Search results are summaries; they do not repeat the complete request or
response. Feedback bodies and representation statuses remain on the authorized
exact-turn read in bounded pages. Follow `feedback.next` and
`representations.next` until they are `null`. Feedback continuations carry
`feedbackOffset`; representation pages carry an opaque `representationCursor`.
Pass the supplied argument back to `get` rather than synthesizing one. The
representation cursor pins a snapshot, so rows appended while paging do not
produce skips or duplicates.
