# OpenMerchant UCP Quotes

`dev.openmerchant.quotes` adds synchronous merchant-authoritative pricing and
availability to the UCP shopping REST service. Its version is
`2026-04-08`, and its machine-readable schema is published at
`https://openmerchant.dev/ucp/2026-04-08/schemas/quotes.json`.

## Discovery and endpoints

A commerce-ready merchant advertises the capability in `/.well-known/ucp`.
The catalog variant's `metadata.quote_url` is the absolute `POST` endpoint.
The returned quote resource supplies its polling URL; `POST` that same URL
with the quote token and a new idempotency key to refresh the saved request.

<!-- BEGIN GENERATED QUOTE OPERATIONS -->
| Protocol | Method | Path | Authorization | Responses |
| --- | --- | --- | --- | --- |
| MPP | `POST` | `/quotes` | `idempotency_key` | 201, 202, 409, 422, 502 |
| MPP | `GET` | `/quotes/{quote_id}` | `quote_access_token` | 200, 404 |
| MPP | `POST` | `/quotes/{quote_id}` | `quote_token_and_idempotency_key` | 200, 202, 404, 409, 422, 502 |
| UCP | `POST` | `/quotes` | `ucp_signed_platform_and_idempotency_key` | 201, 202, 409, 422, 502 |
| UCP | `GET` | `/quotes/{quote_id}` | `ucp_signed_platform_and_quote_token` | 200, 404 |
| UCP | `POST` | `/quotes/{quote_id}` | `ucp_signed_platform_quote_token_and_idempotency_key` | 200, 202, 404, 409, 422, 502 |
<!-- END GENERATED QUOTE OPERATIONS -->

## Creating a quote

The request identifies a catalog item and/or exact `catalog_variant_id`; SKU is
not a quote identifier because it is not globally unique. It may include a
quantity or typed `booking` request, customizations, UCP buyer and pricing
context, a fulfillment constraint with its destination nested at
`fulfillment.address`, and opaque eligibility references under
`context.eligibility`. The server persists the immutable request and requester
identity before sending one signed `quote.requested` event to the designated
merchant responder.

For a listing that requires a shipping or delivery address, the buyer supplies
the matching fulfillment `type` and nests the destination at
`fulfillment.address`. The optional `option_id` selects one advertised option;
omitting it accepts any advertised option of that type. Listings without an
address requirement may omit fulfillment so the responder can return applicable
digital, pickup, delivery, or booking choices.

The API waits up to ten seconds. A normal `201` response contains the real
availability and immutable `offers[]`; a `202` means delivery may have reached
the merchant and includes signed `Location`, `Retry-After: 2`, and `next: poll`.
Each offer has exactly one fulfillment choice, availability, currency, unit
information, expiry, authoritative total, final `tax_treatment`, and
ordered UCP `totals` that reconcile exactly. Booking offers pin the exact
selection. Quantity offers support non-bookable merchant-managed inventory.

Creation and refresh never hold or decrement inventory. A cart or checkout
selects a concrete offer ID; actual local, merchant, or provider reservation
occurs only in the purchase flow. Completed quote responses may be cached, but
refresh always bypasses the cache.

## Refresh and errors

Refresh has no request body and reuses every saved input. It starts a new
resolution generation, preserves old offers as immutable superseded records,
and returns new offer IDs. Price, subtotal composition, fulfillment, and
availability changes are explicit; an old offer never silently becomes a new
price. A pending generation remains pollable instead of emitting a competing
event. Declined, input-required, and consumed quotes require a new quote.

The dedicated opaque quote access token is distinct from `Idempotency-Key`, is
covered by the UCP response signature, and is never placed in JSON or URLs.
The verified UCP platform identity, mode, merchant, quote ID, and token must all
match; every mismatch has the same not-found result.
