Conversation
Adds `dev.ucp.shopping.ask`, a new top-level shopping capability. A platform,
on behalf of the buyer, asks a free-form question and receives a text answer
with optional related links. `ask` is the standardized ingress for the open
questions buyers actually ask — returns, shipping, warranty, store hours, fit,
suitability — and for business-specific facts and knowledge that no structured
capability exposes.
Why a dedicated capability: catalog, cart, checkout, and order answer questions
that map to a structured operation, and they should own those. `ask` is their
open-question complement — a question that maps to a dedicated operation is
owned by that capability, while `ask` covers everything stated as a free-form
question. Modeling it as a first-class capability, rather than burying Q&A
inside catalog, keeps routing honest: a classifier picks `ask_business` for
open questions and the structured tools for their own lanes.
Request — a `query`, with optional `ids` to ground it and platform `context`:
{
"query": "What is your return policy for these on sale?",
"ids": ["https://business.example.com/products/winter-jacket"],
"context": { "address_country": "US", "language": "en" }
}
Response — an always-populated `answer` (best-effort), optional `links`, and
optional `conversation` / `messages`:
{
"ucp": { "version": "...", "capabilities": { "dev.ucp.shopping.ask": [...] } },
"answer": {
"markdown": "Sale items can be returned within **14 days** for store credit..."
},
"links": [
{ "type": "refund_policy", "url": ".../policies/refunds", "title": "Refund Policy" }
]
}
Design highlights:
- Best-effort contract: every well-formed, authorized, in-limits request returns
success with a populated `answer`. A "can't answer" outcome is a populated
answer that says so plainly — never an error code or non-2xx status.
- Multi-turn: a business MAY issue an opaque `conversation` GID; the platform
replays it to continue, and the business builds on prior turns. Omitting it
starts fresh; an unresolved GID starts a new conversation and notes it in
`messages`. Platforms MUST treat the GID as opaque.
- Grounding via `ids`: a business SHOULD accept grounding identifiers (product
and variant IDs are the recommended minimum) and MAY also accept secondary
identifiers (SKU, handle, URL) and other held GIDs such as a cart or checkout.
For resources like a cart or checkout, a business MAY accept the held GID as a
bearer reference — possession is enough, with no separate authentication — and
MAY otherwise require authentication to reach a resource. Questions needing a
buyer's private account data (e.g. order history) are out of scope for this
version.
- Messages: non-blocking notes from the shared vocab — `info`/`not_found` when a
referenced item can't be resolved, warnings with `presentation: "disclosure"`
for notices the platform MUST surface.
- Reuses existing leaf types (context, signals, attribution, description, link,
message); introduces no new shared types, enums, or monetary fields.
Transport: MCP tool `ask_business` and REST `POST /ask`; scope
`dev.ucp.shopping.ask:read` for personalized answers. Includes the schema,
OpenRPC/OpenAPI bindings, capability docs (overview, MCP, REST), worked
single-turn and multi-turn examples, and mkdocs nav.
| grounded against specific items ("is this washable?"); without them, it applies | ||
| to the storefront broadly ("what is your return policy?"). | ||
|
|
||
| `ids` is **optional**. A business **SHOULD** accept the identifiers that ground a |
There was a problem hiding this comment.
I'm curious at the choice to model this as an array of possibly varied types of identifiers. I'd guess platforms will typically know what type of ID they'd be including and that it would be advantageous to send that context along to businesses (and their AI agents) as well e.g.
"ids": {
"dev.ucp.product.id": [...],
"dev.ucp.variant.url": [...]
}
If users are asking a business about products (one of the likely use cases noted) as similar structure could be used in the response to identify product / variant ids that could be immediately leveraged for catalog, cart or checkout capabilities.
There was a problem hiding this comment.
UCP contract is that ids are self-describing GIDs (e.g. gid://Shopify/Product/...). The platform treats them as opaque IDs, but the business can differentiate between a product, variant, cart, etc. This allows us to keep single array and provide an array of grounding references -- which is a nice property.
For URLs it's a good flag, I'm borrowing from the contract we adopted in catalog lookup operation where we allow GIDs and other identifiers that business can recognize (e.g. GTIN, URL, etc). It is overloading ids but I'd like to see how far we can push it; URLs are also self-describing, which keeps the overall contract more or less consistent for the business.
| sufficient to ask about that resource, with no separate authentication. Access is | ||
| otherwise the business's to govern: it **MAY** require authentication to reach a | ||
| given resource (a gated catalog, for example). | ||
|
|
There was a problem hiding this comment.
This could very well fall outside the scope of a "v1" of this capability ... but just to explore, what about an attachments property as another way to scope / ground / contextualize the conversation? e.g.:
- Do you have a sweater with patterns like this available?
- Can I get a refund when the lace on my shoe broke after 3 weeks of normal wear?
attachments: [<jpg data url>]
This could be useful where people are very comfortable snapping pictures from their phones as well as possible wearables use cases.
There was a problem hiding this comment.
Great callout. Multi-modal input is definitely something we should model and consider, but agree that this may be a good v2 iteration: it'll likely require exposing a config on business schema, so we can communicate what type of attachments (image, audio, video, ...) are supported, etc. Added a note for this in the draft.
As an aside, "find me something like this image" is better handled in catalog as a visual-similarity input. As a live example, Shopify global catalog extension provides similarity search that accepts ID or image input.
|
|
||
| | Scope | Description | | ||
| | :--- | :--- | | ||
| | `dev.ucp.shopping.ask:read` | Ask on behalf of the authenticated user — personalized answers reflecting member pricing, entitlements, or gated availability. | |
- `conversation` becomes an object (`id` + optional `expires_at`, mirroring
cart/checkout) instead of a bare string — room to grow.
- Access reframed as a credential spectrum (public / resource reference /
business posture / authenticated user), replacing the ad-hoc bearer + scope
wording and resolving the `:read` vs out-of-scope contradiction.
- `ids` framing cites catalog lookup's contract; product/variant the
recommended minimum.
- Future-direction note for `attachments` (multimodal grounding).
`link.json` gains an optional `id` (a GID). When an answer recommends an
addressable resource — e.g., a product — its link can now carry the `id`
alongside the `url`: `url` for display, `id` to act on the resource through
the capability that owns it (resolve in catalog, add to cart) without
re-deriving it from the answer prose. The field is optional and on the
shared link type, so it's non-breaking for cart/checkout/variant.
Rewrites the ask Links section to match: links are the addressable
references to the resources an answer names; a business SHOULD return one
per referenced resource, with a `title` that captures the resource and, for
an addressable resource, its `id`.
|
@jamesandersen great feedback, ty! Updated the draft, ptal. |
Adds
dev.ucp.shopping.ask, a new top-level shopping capability. A platform, on behalf of the buyer, asks a free-form question and receives a text answer with optional related links.askis the standardized ingress for the open questions buyers actually ask — returns, shipping, warranty, store hours, fit, suitability, etc — and for business-specific facts and knowledge that no structured capability exposes.Examples
A broad question, no grounding. Request:
{ "query": "What is your return policy for items bought on sale?" }Response:
{ "ucp": { "version": "draft", "capabilities": { "dev.ucp.shopping.ask": [{ "version": "draft" }] } }, "answer": { "markdown": "Sale items can be returned within **14 days** of delivery for store credit. Items returned to a different region may be subject to local return rules — see the refund policy for details." }, "links": [ { "type": "refund_policy", "url": "https://business.example.com/policies/refunds", "title": "Refund Policy" } ] }Grounded, multi-turn conversation
Turn 1 grounds the question on a specific product via
ids; the business issues an opaqueconversationvalue. Turn 2 replays it to continue, and the business builds on the prior turn. Request:{ "query": "Is this jacket warm enough for winter?", "ids": ["https://business.example.com/products/winter-jacket"] }Response:
{ "ucp": { "version": "draft", "capabilities": { "dev.ucp.shopping.ask": [{ "version": "draft" }] } }, "answer": { "plain": "Yes — it's insulated for sub-zero conditions and rated for harsh winter use." }, "conversation": "conv_9a3f2e7b" }Turn 2, Request:
{ "query": "And is it waterproof?", "conversation": "conv_9a3f2e7b" }Turn 2, response:
{ "ucp": { "version": "draft", "capabilities": { "dev.ucp.shopping.ask": [{ "version": "draft" }] } }, "answer": { "plain": "It's water-resistant with a durable water-repellent finish — good for snow and light rain, though not fully waterproof." }, "conversation": "conv_9a3f2e7b" }"Can't answer" (best-effort)
A question the business can't address still returns a successful response with a populated
answerthat states the limitation — never an error.{ "ucp": { "version": "draft", "capabilities": { "dev.ucp.shopping.ask": [{ "version": "draft" }] } }, "answer": { "plain": "I don't have information about competitor pricing. For questions about this store's products, I can help with policies, materials, fit, and availability." } }Design highlights
answer. A "can't answer" outcome is a populated answer that says so plainly, never an error code or non-2xx status.conversationvalue; the platform replays it to continue, and the business builds on prior turns. Omitting it starts fresh; an unresolved value starts a new conversation and notes it inmessages. Platforms MUST treat it as opaque.ids— a business SHOULD accept grounding identifiers (product and variant IDs are the recommended minimum) and MAY also accept secondary identifiers (SKU, handle, URL) and other held GIDs such as a cart or checkout. For a cart or checkout, a held GID MAY act as a bearer reference — possession is enough, with no separate authentication — and a business MAY otherwise require authentication.askstays independent ofcatalog.info/not_foundwhen a referenced item can't be resolved, warnings withpresentation: "disclosure"for notices the platform MUST surface.context,signals,attribution,description,link,message); introduces no new shared types, enums, or monetary fields.Checklist