From 3119aa918031bbe6d01a342cc393528ef1d7300a Mon Sep 17 00:00:00 2001 From: Venkatesh Hodavdekar Date: Wed, 11 Mar 2026 17:04:06 +0000 Subject: [PATCH] feat: add Return Extension to UCP specification This change introduces the Return Extension, allowing businesses to communicate return conditions, methods, timelines, and costs. Key changes: - Added JSON schemas for Return Policy, Method, and Fee. - Extended Checkout and Order with return policies. - Renamed Return Policy 'category' to 'return_window_type'. - Added specification documentation and updated site navigation. Closes #TBD --- docs/specification/return.md | 142 ++++++++++++++++++ mkdocs.yml | 1 + source/schemas/shopping/return.json | 60 ++++++++ source/schemas/shopping/types/return_fee.json | 23 +++ .../schemas/shopping/types/return_method.json | 19 +++ .../schemas/shopping/types/return_policy.json | 37 +++++ 6 files changed, 282 insertions(+) create mode 100644 docs/specification/return.md create mode 100644 source/schemas/shopping/return.json create mode 100644 source/schemas/shopping/types/return_fee.json create mode 100644 source/schemas/shopping/types/return_method.json create mode 100644 source/schemas/shopping/types/return_policy.json diff --git a/docs/specification/return.md b/docs/specification/return.md new file mode 100644 index 00000000..0f282133 --- /dev/null +++ b/docs/specification/return.md @@ -0,0 +1,142 @@ + + +# Return Extension + +## Overview + +The Return Extension allows businesses to communicate the conditions, methods, timelines, and costs associated with returning physical items directly to the platform and the buyer, mirroring real-world commerce requirements. + +By exposing the return policy natively in the UCP schema, AI agents and platforms can intelligently answer user queries like *"Can I return this in-store?"* or *"How many days do I have to return this?"* without forcing the user to leave the platform to hunt for a policy on the merchant's website. + +This extension adds a `return_policies` field to Checkout containing: + +* `return_policies[]` — conditions governed by the merchant for specific items. + * `return_window_type` — the category of return window (finite, lifetime, final sale, etc.) + * `return_days` — the number of days in the window. + * `methods[]` — permitted physical methods (in-store, by-mail, etc.) + * `fee` — the cost structure for that specific method. + +**Mental model:** + +* `return_policies[0]` Standard Apparel + * `line_item_ids` 👕👖 + * `return_window_type` = `finite_window` 🗓️ 30 Days + * `methods[0]` In-Store 🏬 + * `fee` = `free` ✅ + * `methods[1]` By Mail 📦 + * `fee` = `fixed_fee` $5.00 💸 +* `return_policies[1]` Final Sale + * `line_item_ids` ⌚ + * `return_window_type` = `final_sale` 🚫 + * `exchanges_allowed` = `false` + +## Schema + +Return policies apply to physical items in a checkout session. Items not governed by a specific policy (e.g., digital services) may be omitted or covered by a default policy. + +### Properties + +{{ extension_fields('return', 'return_policies') }} + +### Entities + +#### Return Policy + +{{ schema_fields('types/return_policy', 'return') }} + +#### Return Method + +{{ schema_fields('types/return_method', 'return') }} + +#### Return Fee + +{{ schema_fields('types/return_fee', 'return') }} + +## Rendering + +Return policies are designed for proactive disclosures by the merchant. Platforms use these fields to provide transparency about the logistical requirements of a purchase before completion. + +### Human-Readable Fields + +| Location | Field | Required | Purpose | +| ---------------------- | -------------- | -------- | --------------------------------------------------- | +| `return_policy` | `return_days` | No | Quantitative window for the return. | +| `return_method.fee` | `display_text` | No | Context for the fee (e.g., "Restocking Fee"). | +| `return_method.fee` | `amount` | No | Price in minor units for fixed fees. | + +### Business Responsibilities + +**For `return_window_type`:** + +* **MUST** accurately reflect the merchant's legal and commercial policy. +* **MUST** provide `return_days` if the type is `finite_window`. + +**For `return_method.fee`:** + +* **SHOULD** use `display_text` to explain the nature of the fee (e.g., "Prepaid Label", "Restocking Fee"). +* **MUST** provide `amount` if the type is `fixed_fee`. + +### Platform Responsibilities + +Platforms **SHOULD** use return policies to answer buyer questions and provide assurance: + +* Surface "Final Sale" warnings early in the checkout flow. +* Answer specific questions like "Is return shipping free?" by inspecting the `by_mail` method fee. +* Use `return_days` to calculate and display the specific return deadline based on the delivery date. + +## Examples + +### Mixed Cart + +In this example, apparel items have a standard window, while a custom item is final sale. + +```json +{ + "return_policies": [ + { + "id": "rp_apparel", + "line_item_ids": ["shirt", "pants"], + "return_window_type": "finite_window", + "return_days": 30, + "exchanges_allowed": true, + "methods": [ + { + "type": "in_store", + "fee": { + "type": "free", + "display_text": "Free In-Store Return" + } + }, + { + "type": "by_mail", + "fee": { + "type": "fixed_fee", + "amount": 500, + "display_text": "Return Shipping Fee" + } + } + ] + }, + { + "id": "rp_final_sale", + "line_item_ids": ["custom_engraved_watch"], + "return_window_type": "final_sale", + "exchanges_allowed": false + } + ] +} +``` diff --git a/mkdocs.yml b/mkdocs.yml index 563de73a..47185d6c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,6 +47,7 @@ nav: - Buyer Consent Extension: specification/buyer-consent.md - Discounts Extension: specification/discount.md - Fulfillment Extension: specification/fulfillment.md + - Return Extension: specification/return.md - Cart Capability: - Overview: specification/cart.md - HTTP/REST Binding: specification/cart-rest.md diff --git a/source/schemas/shopping/return.json b/source/schemas/shopping/return.json new file mode 100644 index 00000000..10295a03 --- /dev/null +++ b/source/schemas/shopping/return.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ucp.dev/schemas/shopping/return.json", + "name": "dev.ucp.shopping.return", + "title": "Return Extension", + "description": "Extends Checkout with return policy support.", + "$defs": { + "return_policy": { + "$ref": "types/return_policy.json" + }, + "return_method": { + "$ref": "types/return_method.json" + }, + "return_fee": { + "$ref": "types/return_fee.json" + }, + "dev.ucp.shopping.checkout": { + "title": "Checkout with Return", + "description": "Checkout extended with return policies.", + "allOf": [ + { + "$ref": "checkout.json" + }, + { + "type": "object", + "properties": { + "return_policies": { + "type": "array", + "items": { + "$ref": "#/$defs/return_policy" + }, + "description": "Return policies applicable to items in the checkout.", + "ucp_request": "omit" + } + } + } + ] + }, + "dev.ucp.shopping.return": { + "platform_schema": { + "title": "Return Capability (Platform)", + "description": "Platform-level return capability configuration", + "allOf": [ + { + "$ref": "../capability.json#/$defs/platform_schema" + } + ] + }, + "business_schema": { + "title": "Return Capability (Business)", + "description": "Business-level return capability configuration", + "allOf": [ + { + "$ref": "../capability.json#/$defs/business_schema" + } + ] + } + } + } +} diff --git a/source/schemas/shopping/types/return_fee.json b/source/schemas/shopping/types/return_fee.json new file mode 100644 index 00000000..d958012e --- /dev/null +++ b/source/schemas/shopping/types/return_fee.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ucp.dev/schemas/shopping/types/return_fee.json", + "title": "Return Fee", + "description": "The cost structure associated with a specific return method.", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "type": "string", + "enum": ["free", "fixed_fee", "customer_responsibility"], + "description": "The cost structure for the return method." + }, + "amount": { + "$ref": "amount.json", + "description": "Fixed return fee charged by the merchant, represented in minor currency units (e.g., cents). Required if type is fixed_fee." + }, + "display_text": { + "type": "string", + "description": "Human-readable text to display against the fee to provide context to the buyer (e.g., 'Restocking Fee', 'Return Shipping Label')." + } + } +} diff --git a/source/schemas/shopping/types/return_method.json b/source/schemas/shopping/types/return_method.json new file mode 100644 index 00000000..8ed1434a --- /dev/null +++ b/source/schemas/shopping/types/return_method.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ucp.dev/schemas/shopping/types/return_method.json", + "title": "Return Method", + "description": "The physical method through which the buyer can return the item.", + "type": "object", + "required": ["type", "fee"], + "properties": { + "type": { + "type": "string", + "enum": ["in_store", "by_mail", "kiosk"], + "description": "The physical method through which the buyer can return the item." + }, + "fee": { + "$ref": "return_fee.json", + "description": "The cost structure associated with this specific return method." + } + } +} diff --git a/source/schemas/shopping/types/return_policy.json b/source/schemas/shopping/types/return_policy.json new file mode 100644 index 00000000..9c4ff5a2 --- /dev/null +++ b/source/schemas/shopping/types/return_policy.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://ucp.dev/schemas/shopping/types/return_policy.json", + "title": "Return Policy", + "description": "Conditions, methods, timelines, and costs associated with returning physical items.", + "type": "object", + "required": ["id", "line_item_ids", "return_window_type"], + "properties": { + "id": { + "type": "string", + "description": "Unique identifier for the return policy." + }, + "line_item_ids": { + "type": "array", + "items": { "type": "string" }, + "description": "Line items governed by this return policy, allowing distinct policies per item." + }, + "return_window_type": { + "type": "string", + "enum": ["lifetime", "no_returns", "final_sale", "finite_window"], + "description": "The type of return window." + }, + "return_days": { + "type": "integer", + "description": "Number of days allowed for a return, typically starting from the date of delivery. Required if category is finite_window." + }, + "exchanges_allowed": { + "type": "boolean", + "description": "Indicates whether the buyer can exchange the item." + }, + "methods": { + "type": "array", + "items": { "$ref": "return_method.json" }, + "description": "Permitted physical methods for returning the item, along with their associated fee structures." + } + } +}