ai.commercestudio.b2b.quote
Draft, version 2026-10-19. Extends dev.ucp.shopping.cart and dev.ucp.shopping.checkout (UCP 2026-08-25). Schema: https://b2b.commercestudio.ai/ucp/schemas/quote.json.
A buyer's agent can ask the seller for a quote on a cart instead of buying at the buyer's contract prices. A person on the seller's side prices it. Once the buyer accepts, checkout from that cart runs at the quoted prices.
UCP has no quote capability yet. This extension follows the field names of the open proposal ucp#929: status, id, expires_at and message, plus proposal and accepted_id on update. If that proposal is adopted, implementations can move to it with little change.
Principles
- The seller sets the price. A buyer's target price is a request. Only the seller's quoted price is ever charged.
- No new operations. Requests, acceptance and withdrawal ride on Update Cart. While the seller works on a quote, the cart carries an Action of type
ai.commercestudio.b2b.quote. Per UCP's Actions rules, an outstanding Action never blocks other cart edits. - Agents that don't negotiate the extension never see it. Their carts and checkouts are standard UCP.
Response field: b2b_quote
On a cart, b2b_quote is always present when the extension is negotiated.
| Field | Meaning |
|---|---|
round |
Negotiation round: 1 for the request, plus one for each counter-offer |
thread[] |
The negotiation so far, oldest first: from (buyer or seller), at, text |
status |
available or not_available (whether this cart may ask for a quote), pending (the seller is working on it), quoted (prices are ready), accepted (the cart is at the quoted prices), expired, withdrawn, or ordered (on a checkout, once the order is placed) |
threshold |
Carts with a subtotal at or above this may ask for a quote |
id, reference |
The quote id (send it back as accepted_id) and the name people use, e.g. quote QN000008 |
requested_at, quoted_at, expires_at |
RFC 3339 times |
message |
The seller's latest note to the buyer |
po_number |
The buyer's PO on the quote, to use at checkout |
lines[] |
item_id, title, quantity, contract_price, requested_price (the buyer's target), quoted_price |
contract_subtotal, quoted_subtotal, savings_vs_contract |
Minor units |
When ai.commercestudio.b2b.pricing is also negotiated and a quote is accepted, each b2b_pricing.lines[] entry carries quoted_price next to contract_price, and the totals gain quoted_total. line_items[].item.price is the price the buyer pays.
Requests: b2b_quote on Update Cart
Update Cart stays a full replacement: send line_items as usual, plus one of these.
| Field | Effect |
|---|---|
proposal: { message?, po_number?, target_prices?: [{ item_id, price }] } |
Ask for a quote on the cart as it stands. po_number goes on the quote (B2B Edition's reference number) and is offered again at checkout (quote_po). Refused with quote_not_available under the threshold, and with quote_exists if a quote is already open |
accepted_id |
Accept a quoted quote. The business re-checks stock; if it can't fill the quote as quoted, it refuses with quote_unavailable |
withdraw: true |
Withdraw the open quote |
Changing the cart's lines withdraws an open or accepted quote, with a quote_changed warning. Re-sending the same lines keeps it.
Negotiation
A quote is a negotiation, often over several rounds.
- Counter-offers. A
proposalon a cart whose quote is already open, or already accepted, is a counter-offer on the same quote: a note and/or new target prices. It must carry at least one of them, or it's refused withinvalid_counter_offer. The quote goes back topending, androundgoes up by one. The seller's last offer stays inlines[].quoted_pricewhile they consider it, andquote_counteredconfirms the counter-offer was sent. - Countering an accepted quote withdraws the acceptance. The cart goes back to contract prices until the seller answers.
- Whose turn it is follows the conversation:
- Before any counter-offer, the seller has answered once they change a price, send a note, or enable checkout.
- After a counter-offer, only a newer seller message or changed prices count.
- The thread.
thread[]lists every message, oldest first:{ from: "buyer" | "seller", at, text }. - Repricing after acceptance. If the seller changes prices after the buyer accepted, the acceptance is withdrawn (
quote_updated) and the buyer reviews the new prices.
Action
While status is pending, actions["ai.commercestudio.b2b.quote"] holds one entry:
idis the quote id;configis{ state: "pending", reference, requested_at, expires_at }.
The Action is removed once the seller answers.
Checkout
Create Checkout with the cart's cart_id uses the accepted quote's prices while the quote is valid and the business can fill it as quoted. Otherwise it returns quote_not_applied and contract prices. The checkout shows b2b_quote with status: accepted, which becomes ordered once the order is placed.
Changing a checkout's lines drops the quote (quote_changed). If the quote expires before Complete Checkout, the business re-prices at contract and returns quote_expired (severity requires_buyer_input), so the buyer confirms the new total.
Approval, credit and payment rules apply to the quoted total exactly as to any other.
Messages
| Code | Type | When |
|---|---|---|
quote_available |
info | The cart may ask for a quote |
quote_requested |
info | Request accepted. An authoritative confirmation for the agent to show the buyer |
quote_countered |
info | Counter-offer sent; the seller's turn. Also an authoritative confirmation |
quote_updated |
warning | The seller repriced after acceptance; the cart is back at contract prices |
quote_ready |
info | The seller priced the quote |
quote_accepted |
info | The cart is at the quoted prices |
quote_changed, quote_expired, quote_withdrawn, quote_not_applied |
warning or error | The quote no longer applies |
quote_not_available, quote_exists, quote_not_ready, quote_not_found, quote_unavailable, invalid_target_price |
error (recoverable) | The request can't be done |
Finding quotes later
UCP has no list operations, and a new conversation doesn't know the cart id. So Callisto offers a Commerce Studio operation: list_quotes (MCP) or GET /api/b2b/quotes (REST). It returns the buyer's quote requests with their carts and current status.
Callisto on BigCommerce B2B Edition
Behaviour verified on the Wildleaf demo store on 2026-10-23:
- Request. A request creates a B2B Edition quote (
POST /api/v3/io/rfq) for the buyer's company. Lines are at contract price with checkout off, and the buyer, their role, the target prices and the note go in the quote's message. - Answer. A sales rep answers in the control panel by editing prices and/or sending a note; checkout may stay off (QN000012). Callisto reads the quote on Get Cart. Status 5 means expired, 6 archived (withdrawn) and 4 ordered.
- Counter-offers. A counter-offer is posted as a message on the quote; a message-only update leaves the rep's prices alone (verified on QN000013). B2B Edition credits every message to the API account's owner, so Callisto keeps a record of its own messages to tell the buyer's from the rep's.
- Placing the order. Quoted orders go through Callisto's own order path, with list prices pinned to the quoted prices. Callisto then links the order to the quote (
POST /rfq/{id}/ordered), which sets the quote to Ordered. The staff note names the quote. - First live run: quote QN000008 → order #113.
- The quote was for 25 Navy / M hoodies and 10 quarter-zips, and the rep priced them at $92.50 and $105.00, down from $100.00 and $111.96.
- The order came to $3,412.50 including shipping.
- Spaulding Smails requested it, and Judge Smails approved and placed it.