com.centaurapps.shopping.fitment — UCP extension specification (v0.3)
Extends: dev.ucp.shopping.checkout
Schema: https://ucp.centaurapps.com/schemas/fitment.json
What this extension does
Products can carry fitment rules. A shop defines its own selection
levels (for example Year / Make / Model, or Brand / Model; keys l1,
l2, ...) and a product's rules say which selections it fits and which
questions must be answered before compatibility is known. A business
declaring this extension verifies fitment before sale and escalates checkouts
it cannot verify.
The extension adds two string maps:
| Field | Meaning |
|---|---|
levels |
The buyer's selections, keyed by level key, e.g. { "l1": "2020" }. Values are copied from the level options. |
answers |
Answers to the product's questions, keyed by question key, e.g. { "bed": "5 ft 8 in" }. Values are the chosen option's value, verbatim. |
There is no vehicle object: the levels are whatever the shop configured.
Operations (MCP tools / REST)
| Operation | Input | Output |
|---|---|---|
get_fitment_questions |
product ref, levels? |
the shop's levels with current options and, once enough levels are given, the questions. Status needs_level, does_not_fit or ready. |
check_fitment |
product ref, levels, answers? |
verdict fits, does_not_fit, unknown, needs_answer or needs_level |
evaluate_add_to_cart |
product ref, levels, answers? |
action ADD, WARN, WITHHOLD or ASK, with the text recorded on the cart line |
get_buyer_link |
items[] of { variantId, quantity, productId?, levels?, answers? } |
links[], one per protected item (see below) |
A product ref is productId, variantId, sku or handle. Transport
bindings are published in the business's UCP profile under this capability's
config.transports.
Checkout behavior
When a cart line is protected, the shop's checkout validation reads the
answers carried on the line (_fitment_answers, below) and decides:
- Verified fit → checkout proceeds.
- Wrong fit, unanswered question or missing answers → the checkout ends
requires_escalation. The message is inmessages[]astype: error,code: validation_custom,severity: requires_buyer_input, incontentwith nopath. The text names the question and its options, so an agent can relay it to the buyer word for word. The checkout also carries acontinue_url.
Observed on a live checkout (UCP 2026-08-25): the validation function does
run on UCP checkouts and its message reaches the agent.
Why there is a buyer link
UCP checkout line items accept only id, quantity and item.id. They carry
no line attributes, and neither does the cart schema, so an agent cannot
deliver the fitment answers through UCP. For a protected product the checkout
therefore stays requires_escalation however it is called. A legacy /cart
permalink does carry line properties, and a link of that form cleared the block
in a browser. get_buyer_link builds those links for the agent to hand to the
buyer.
Buyer link
get_buyer_link takes everything in the buyer's cart and returns
{ links: [{ variantId, url, extras }] }.
- One link per protected item. Shopify applies a cart link's
propertiesto the first product in the link only, so each protected item gets its own link with itself first. Items without rules ride on the first link, after the protected item, and are listed in itsextras. With several protected items the buyer completes several separate orders, one per link; tell them so. - URL form:
https://<shop>/cart/<variantId>:<qty>[,<variantId>:<qty>...]?properties=<base64url JSON>(the shop's own domain, not an owned-by-us host). - Properties (the JSON object that is base64url-encoded):
_fitment_answers: JSON text{"levels": {...}, "answers": {...}}with string values only, at most 1,024 characters, starting with{. Checkout reads a longer value as no answers and blocks. If the answers do not fit, no link is made and an error says so.Fitment: a buyer-visible summary of the selection and answers, e.g.2020 · Hyundai · Elantra — Built in: I don't know.
- Refusals: a part that does not pass (
WITHHOLDorASK) gets no link. An item sent withoutproductIdis refused unless the variant itself carries rules, since the service cannot tell whether it is protected. A cart with no protected item gets no link; add those items normally. /buylinks do not support properties, so they cannot be used.
Agent guidance
- Call
get_fitment_questions, then ask the buyer for each level and answer. - Call
evaluate_add_to_cartBEFORE adding any part to a cart. OnASK, relay the question verbatim and retry with the answer; onWITHHOLD, do not add. - Do not try to complete checkout for a protected part. Call
get_buyer_linkwith the samelevelsandanswers, always sendingproductId, and give the buyer each link to open in their browser. - Agents that read none of this still meet the checkout validation, whose message lists the question and options.