Fulfillment Extension¶
Overview¶
The fulfillment extension enables businesses to advertise support for physical goods fulfillment (shipping, pickup, etc).
This extension adds a fulfillment field to Checkout and/or Catalog:
- Checkout (
dev.ucp.shopping.checkout) — selection and cost: which items go where, by which method, at what price and ETA. - Catalog (
dev.ucp.shopping.catalog.searchanddev.ucp.shopping.catalog.lookup) — discovery: a variant advertises the fulfillment options available for it, based on the provided buyer context. See Catalog Discovery.
On Checkout, the fulfillment field contains:
methods[]— fulfillment methods applicable to cart items (shipping, pickup, etc.)line_item_ids— which items this method fulfillsdestinations[]— where to fulfill (address, store location)groups[]— business-generated packages, each with selectableoptions[]
available_methods[]— inventory availability per item (optional)
Mental model:
methods[0]Shippingline_item_ids👕👖selected_destination_id=destinations[0].id🔘✅ 123 Fake Stgroups[0]📦👕👖selected_option_id=options[0].id🔘✅ Standard $5options[1]🔘 Express $10
methods[1]Pick Up in Storeline_item_ids👞selected_destination_id=destinations[0].id🔘✅ Uptown Storegroups[0]📦👞selected_option_id=options[0].id🔘✅ In-Store Pickup
Location Context¶
Base Context defines an optional location: a
stable, opaque Location identifier in the Business's
namespace. The field appears on Catalog requests and on Cart and Checkout
create and update requests (see Checkout Context).
Fulfillment does not add or redefine the field; when the Fulfillment
extension is active, it defines the field's effects.
When Fulfillment extends Catalog or Checkout, context.location on a request
to that capability names the provisional Business Location that the Business
evaluates fulfillment availability against, and generates initial fulfillment
choices for. It does not select a fulfillment destination.
These fields carry distinct roles, not precision levels of one value:
| Field | Role |
|---|---|
context.location |
Provisional Business Location to evaluate fulfillment against. |
Catalog filters.fulfills_to |
Explicit destination items are fulfilled to, and a filter on results. |
Catalog methods[].location |
The Business Location resolved for a place-based method (e.g. pickup) on a variant. |
Checkout methods[].selected_destination_id |
The destination selected from that method's destinations[]. |
Precedence is scoped to what each field governs:
- When Catalog
filters.fulfills_tois present, a Business MUST resolve the fulfillment destination and methodavailabilityfrom it rather than fromcontext. Othercontextfields are unaffected. - Once a Platform sets
selected_destination_idon a Checkout method, a Business MUST use the referenced destination rather thancontext.locationfor that method's fulfillment scope. Other methods are unaffected.
Carrying a Location forward. The same identifier can appear in several of
these fields without collapsing their roles. A Catalog response can report a
place-based method at loc_123. Because base Cart Context already includes
location, loc_123 can travel forward as context.location on a Cart
request. Cart-to-Checkout conversion
initializes the Checkout from the Cart's context, so the Business MAY
use loc_123 to generate an initial Checkout fulfillment destination. Once
the Platform sets selected_destination_id, that explicit selection governs.
Schema¶
Fulfillment applies only to items requiring physical delivery. Items not requiring fulfillment (e.g., digital goods) do not need to be assigned to a method.
Properties¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| fulfillment | Fulfillment | Optional; omitted on complete | Fulfillment details. |
Entities¶
Fulfillment¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| methods | Array[Fulfillment Method] | Optional | Fulfillment methods for cart items. |
| available_methods | Array[Fulfillment Available Method] | Optional | Inventory availability hints. |
Fulfillment Method¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| id | string | Required | Unique fulfillment method identifier. |
| type | string | Required | Fulfillment method type. Well-known values: shipping, pickup. Businesses MAY use additional values. |
| line_item_ids | Array[string] | Required | Line item IDs fulfilled via this method. |
| destinations | Array[Fulfillment Destination] | Optional | Available destinations for this method. In Business responses, each destination carries a type and id. |
| selected_destination_id | ['string', 'null'] | Optional | ID of the selected destination. Accepts any stable, Business-scoped ID the Business recognizes for this method, including Location IDs not yet enumerated in destinations. |
| groups | Array[Fulfillment Group] | Optional | Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method. |
Fulfillment Destination¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| type | string | Required | Destination contract discriminator. Required in Business responses and optional in Platform requests. Well-known values: shipping_address, business_location. The enclosing method contract defines request defaults and which fields the Platform may write; negotiated extensions define additional values. |
| id | string | Required | Fulfillment destination identifier. |
Shipping Destination¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| extended_address | string | Optional | An address extension such as an apartment number, C/O or alternative name. |
| street_address | string | Optional | The street address. |
| address_locality | string | Optional | The locality in which the street address is, and which is in the region. For example, Mountain View. |
| address_region | string | Optional | The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division. |
| address_country | string | Optional | The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example "US". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as "SGP" or a full country name such as "Singapore" can also be used. |
| postal_code | string | Optional | The postal code. For example, 94043. |
| first_name | string | Optional | Optional. First name of the contact associated with the address. |
| last_name | string | Optional | Optional. Last name of the contact associated with the address. |
| phone_number | string | Optional | Optional. Phone number of the contact associated with the address. |
| id | string | Required | ID specific to this shipping destination. |
| type | string | Required | Constant = shipping_address. Destination type discriminator. |
Business Location Destination¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| id | string | Required | Stable, opaque, Business-scoped Location identifier. |
| name | string | Required | Buyer-facing, Business-owned display name. |
| address | Postal Address | Optional | Physical address of the location. |
| type | string | Required | Constant = business_location. Destination type discriminator. Response-only. |
Location Summary¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| id | string | Required | Stable, opaque, Business-scoped Location identifier. |
| name | string | Required | Buyer-facing, Business-owned display name. |
| address | Postal Address | Optional | Physical address of the location. |
Fulfillment Group¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| id | string | Required | Group identifier for referencing merchant-generated groups in updates. |
| line_item_ids | Array[string] | Required | Line item IDs included in this group/package. |
| options | Array[Fulfillment Option] | Optional | Available fulfillment options for this group. |
| selected_option_id | ['string', 'null'] | Optional | ID of the selected fulfillment option for this group. |
Fulfillment Option¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| id | string | Required | Unique identifier for this fulfillment option. |
| title | string | Required | Short label that distinguishes this option from its siblings (e.g. 'Standard', 'Express Shipping', 'Curbside Pickup'). |
| description | Description | Optional | Supplementary context for the title (e.g. 'Arrives in 4 business days', 'Arrives Dec 12-15 via FedEx'). Directly renderable; MUST NOT repeat the title. |
| carrier | string | Optional | Carrier name (for shipping). |
| earliest_fulfillment_time | string | Optional | Earliest fulfillment date. |
| latest_fulfillment_time | string | Optional | Latest fulfillment date. |
| totals | Array[Total Response] | Required | Fulfillment option totals breakdown. |
Fulfillment Available Method¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| type | string | Required | Fulfillment method type this availability applies to. Well-known values: shipping, pickup; businesses MAY use additional values. |
| line_item_ids | Array[string] | Required | Line items available for this fulfillment method. |
| fulfillable_on | ['string', 'null'] | Optional | 'now' for immediate availability, or ISO 8601 date for future (preorders, transfers). |
| description | string | Optional | Human-readable availability info (e.g., 'Available for pickup at Downtown Store today'). |
Total¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| type | string | Required | Cost category. Well-known values: subtotal, items_discount, discount, fulfillment, tax, fee, total. Businesses MAY use additional values. |
| display_text | string | Optional | Text to display against the amount. Should reflect appropriate method (e.g., 'Shipping', 'Delivery'). |
| amount | Signed Amount | Required | Monetary amount in the currency's minor unit as defined by ISO 4217. Refer to the currency's exponent to determine minor-to-major ratio (e.g., 2 for USD, 0 for JPY, 3 for KWD). May be negative — the sign is intrinsic to the value (e.g., discounts are negative, charges are positive). |
Postal Address¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| extended_address | string | Optional | An address extension such as an apartment number, C/O or alternative name. |
| street_address | string | Optional | The street address. |
| address_locality | string | Optional | The locality in which the street address is, and which is in the region. For example, Mountain View. |
| address_region | string | Optional | The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division. |
| address_country | string | Optional | The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example "US". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as "SGP" or a full country name such as "Singapore" can also be used. |
| postal_code | string | Optional | The postal code. For example, 94043. |
| first_name | string | Optional | Optional. First name of the contact associated with the address. |
| last_name | string | Optional | Optional. Last name of the contact associated with the address. |
| phone_number | string | Optional | Optional. Phone number of the contact associated with the address. |
Example¶
{
"ucp": { ... },
"id": "...",
"status": "...",
"currency": "...",
"line_items": [ ... ],
"totals": [ ... ],
"links": [ ... ],
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"selected_destination_id": "dest_1",
"destinations": [
{
"type": "shipping_address",
"id": "dest_1",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt", "pants"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard Shipping",
"description": { "plain": "Arrives Dec 12-15 via USPS" },
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express Shipping",
"description": { "plain": "Arrives Dec 10-11 via FedEx" },
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
}
]
}
}
Destinations¶
A fulfillment method's type describes how items are fulfilled, for example
by shipping or pickup. A destination's type describes where fulfillment
occurs, for example at a shipping address or Business Location. The two
discriminators play different roles by direction: in Business responses,
every destination carries a required type, so responses are
self-describing; in Platform requests, the method's contract determines who
authors destinations[] and MAY define a default destination shape when
type is omitted.
By default, the Platform does not write a method's destinations[]; a contract
keyed on the method's type MAY opt into a Platform-writable request
shape:
Method type |
Request destinations[] |
|---|---|
shipping |
Platform-writable. The Platform writes the Buyer's shipping-address facts. |
pickup |
Not Platform-writable. The Business enumerates locations in its response; the Platform selects a location by its Business-scoped ID (see Selection and Location Identity). |
any other method type |
Not Platform-writable (the base default). The Business enumerates destinations in its response; the Platform selects one via selected_destination_id. |
Destination type is required in responses and optional in requests. In
responses, every destination carries type, so destinations remain
self-describing wherever they appear. In requests, destinations[] is omitted
unless a method-specific contract opts into a Platform-writable shape:
- Under a well-known
shippingmethod, every destination is a Shipping Destination: a destination that omitstypedefaults toshipping_address. - Under a well-known
pickupmethod, destinations are response-only; the Platform selects a location viaselected_destination_id(see Selection and Location Identity). - Under any other method type, the base default applies:
destinations[]is response-only, each response destination self-describes through its requiredtypeandid, and the Platform selects one viaselected_destination_id. That method's defining contract — a future revision of this specification or a negotiated extension — MAY narrow the response destination shape or opt into a Platform-writable request shape.
A request that includes destinations[] MUST also include the method's
type. The well-known values are:
| Value | Meaning |
|---|---|
shipping_address |
A Shipping Destination with flat Postal Address fields. |
business_location |
A Business Location Destination identified by a Business-scoped id. |
Additional values are defined by negotiated extensions. Destination fields specific to such a value are validated by the negotiated extension's schema.
Shipping Destination¶
A Shipping Destination can contain the address itself or an id that
references a saved address. The Business can resolve that id from its own
records or through a trusted Credential Provider, such as a digital wallet or
identity provider.
Platform Request¶
{
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
Business Response¶
{
"type": "shipping_address",
"id": "dest_1",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
Business Location Destination¶
Business Location Destinations appear only in responses; destinations[] on a
pickup method is not a request field. The Platform MUST NOT write
Business Location Destinations into destinations[]; it selects a location by
submitting its stable, opaque, Business-scoped ID as
selected_destination_id. The Business MUST return
type: "business_location", id, and its Buyer-facing name, and MAY
return its Postal Address in address.
Platform Request¶
{
"type": "pickup",
"line_item_ids": ["shirt", "pants"],
"selected_destination_id": "loc_downtown"
}
Business Response¶
{
"type": "business_location",
"id": "loc_downtown",
"name": "Downtown Store",
"address": {
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
}
Selection and Location Identity¶
selected_destination_id identifies the selected destination in a method by
its id, and is the sole channel for selecting a Business Location. It accepts
any stable, Business-scoped ID the Business recognizes for that method,
including an ID the Business has not (yet) enumerated in that method's
destinations[].
Selection is source-agnostic. A non-null selected_destination_id MAY
carry an ID the Platform obtained from a Catalog response, a Location search
or lookup, an earlier Checkout, or any other source the Business recognizes
for that method. Where the ID came from does not change the contract below.
When the Business accepts a non-null selected_destination_id, its response:
- MUST carry that same
selected_destination_idvalue on the method; and - MUST include exactly one destination in that same method's
destinations[]whoseidequals it, typed as specified in Destinations — so the response is self-describing whatever the ID's source.
The Business MUST revalidate the selected destination's current
availability and terms; recognizing an ID neither reserves inventory nor
guarantees eligibility. The Business MUST NOT silently substitute another
destination: it MUST NOT return a different selected_destination_id, and
MUST NOT keep the submitted ID while returning a destination that
describes another location. A Business that cannot honor the submitted
selection rejects it rather than replacing it.
When the Business cannot accept a selected_destination_id submitted on
Update Checkout — the ID is not recognized for
that method, or revalidation fails — it follows the general behavior for a
rejected Update: it MUST leave the current Checkout unchanged and
MUST return that Checkout with an error Message with
severity: "recoverable" whose path selects the attempted method's
selected_destination_id (for example
$.fulfillment.methods[0].selected_destination_id). See
Error Handling and
The path Field.
Rendering¶
Fulfillment options are designed for method-agnostic rendering. Platforms do not need to understand specific method types (shipping, pickup, etc.) to present options meaningfully. The business provides precomputed, human-readable fields that platforms render directly.
Human-Readable Fields¶
| Location | Field | Required | Purpose |
|---|---|---|---|
groups[].options[] |
title |
Yes | Primary label that distinguishes from siblings |
groups[].options[] |
description |
No | Supplementary context for the title |
groups[].options[] |
totals |
Yes | Cost breakdown: an array of total objects |
available_methods[] |
description |
No | Standalone explanation of alternative availability |
Business Responsibilities¶
For options[].title:
- MUST distinguish this option from its siblings
- SHOULD include method and speed (e.g., "Express Shipping", "Curbside Pickup")
- MUST be sufficient for buyer decision if
descriptionis absent
For options[].description:
- MUST NOT repeat
titleortotal—provides supplementary context only - SHOULD include timing, carrier, or other decision-relevant details
- SHOULD be a complete phrase (e.g., "Arrives Dec 12-15 via FedEx")
- MAY be omitted if title is self-explanatory
For available_methods[].description:
- MUST be a standalone sentence explaining what, when, and where
- SHOULD be usable verbatim in platform dialogue (e.g., "Pants available for pickup at Downtown Store today at 2pm")
For ordering:
- Businesses SHOULD return
options[]in a meaningful order (e.g., cheapest first, fastest first) - Platforms SHOULD preserve that order, but MAY re-order it (e.g. to match known buyer preferences or surface-specific ranking); they MUST preserve the method/option grouping
Platform Responsibilities¶
Platforms SHOULD treat fulfillment as a generic, renderable structure:
- Render each option as a card using
title,description, andtotal - Present all methods returned—method selection is a buyer decision
- Preserve the method and option structure—do not merge or de-duplicate; the platform chooses ordering
- Use
available_methods[].descriptionto surface alternatives to the buyer
Platforms MAY provide enhanced UX for recognized method types (store
selectors
for pickup, carrier logos for shipping), but this is optional. The baseline
contract is: title + description + total is sufficient to render any
option.
When a buyer selects an option the platform cannot fully process, the
platform SHOULD use continue_url to hand off to the business's checkout.
Available Methods¶
Available methods indicate whether an item can be fulfilled with a given method, and when. Use cases:
- Alternative methods: "These pants are also available for pickup at Downtown Store"
- Fulfill later: Preorders, items shipping from a distant warehouse, pickup when store gets inventory
{
"ucp": { ... },
"id": "...",
"status": "...",
"currency": "...",
"line_items": [ ... ],
"totals": [ ... ],
"links": [ ... ],
"fulfillment": {
"methods": [
{
"id": "shipping",
"type": "shipping",
"line_item_ids": ["shirt", "pants"]
},
{
"id": "pickup",
"type": "pickup",
"line_item_ids": []
}
],
"available_methods": [
{
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"fulfillable_on": "now"
},
{
"type": "pickup",
"line_item_ids": ["pants"],
"fulfillable_on": "2026-12-01T10:00:00Z",
"description": "Available for pickup at Downtown Store today at 2pm"
}
]
}
}
The description field enables platforms to surface alternatives to buyers:
🤖 The shirt and pants ship for $5, arriving in 5-8 days. Or the pants can be picked up at Downtown Store in 4 hours.
If the buyer chooses pickup but the platform doesn't support split
fulfillment, the platform SHOULD use continue_url to hand off to the
business's checkout.
Catalog Discovery¶
When the fulfillment extension extends the Catalog capability, each variant
in a catalog response carries a fulfillment object listing the fulfillment
methods available for that variant and their availability — so a buyer
browsing the catalog can see how an item can be fulfilled.
Methods¶
fulfillment.methods[] lists the methods available for a variant. Each
method has:
type— the fulfillment method (e.g.shipping,pickup); see Method Types.description— short, buyer-facing summary of how the variant is fulfilled via this method (e.g. "Ships in 2–4 business days"). Directly renderable; see Rendering.availability— whether the variant is available via this method at the specified or inferred location.location— for place-based methods such aspickup, the Location resolved for that method and the Business's stable identifier for that place. A Business that advertises pickup at alocationMUST accept that same ID asselected_destination_idfor that method in Checkout. The Business revalidates current availability and terms; discovery neither reserves inventory nor guarantees eligibility. The selection contract is source-agnostic; see Selection and Location Identity.options— concrete fulfillment choices within this method (e.g. Standard, Express); see Options. Optional.
Catalog reports availability for a single location per method — the one
specified via fulfills_to or inferred from context; discovering and
comparing other locations is handled separately.
The variant-level availability indicates whether the variant is
obtainable via any method; a method's own availability is authoritative
for that method. Where a method states availability, consumers MUST use
it for that method and MUST NOT infer per-method availability from the
variant-level value.
Options¶
A method MAY carry options[], a representative subset of its fulfillment
options — not an exhaustive list. Without a destination or full cart, a
Business SHOULD preview meaningful boundary options for the Buyer (e.g.
cheapest, fastest); more specific options are negotiated in Checkout once the
line items and destination are known.
Each option carries an id and a title (a short label distinguishing it
from siblings), plus an optional renderable description for context. These
are a shared base: at checkout the same option is composed with cost and
timing (totals, carrier, fulfillment times). The option is open, so a
business MAY annotate it with additional fields. A method MAY also carry
none, surfacing only type, description, and availability; options are
nested directly under the method, with no group layer (unlike checkout
methods[].groups[].options[]).
A discovered option id lets a Buyer's choice carry forward: a Business
SHOULD accept the same ID as methods[].groups[].selected_option_id in
Checkout. The ID is a best-effort handle, not a guaranteed match — an option
discovered for a single product may differ at Checkout, where other products,
quantities, and combined fulfillment modify the options.
Shapes¶
Catalog Fulfillment¶
How a catalog variant can be fulfilled. Mirrors checkout fulfillment.
| Name | Type | Requirement | Description |
|---|---|---|---|
| methods | Array[object] | Optional | Fulfillment methods for this variant. |
Catalog Fulfillment Method¶
A fulfillment method on a catalog variant: how the variant can be fulfilled, and its availability.
| Name | Type | Requirement | Description |
|---|---|---|---|
| type | string | Required | Fulfillment method type. Well-known values: shipping, pickup. Businesses MAY use additional values. |
| description | object | Optional | Short buyer-facing summary (e.g. 'Ships in 2–4 business days'). |
| availability | object | Optional | Availability of this variant via this method at the specified or inferred location. |
| location | string | Optional | Stable, opaque identifier for the Business Location resolved for this place-based fulfillment method. The Business recognizes the same ID when submitted as selected_destination_id for that method; recognition does not reserve inventory or guarantee eligibility, and current terms are revalidated. |
| options | Array[object] | Optional | Representative fulfillment options for this method (e.g. Standard, Express). Without a destination or full cart, a Business SHOULD preview meaningful boundary options (e.g. cheapest, fastest); more specific options are negotiated in Checkout once line items and destination are known. |
Fulfillment Option Base¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| id | string | Required | Unique identifier for this fulfillment option. |
| title | string | Required | Short label that distinguishes this option from its siblings (e.g. 'Standard', 'Express Shipping', 'Curbside Pickup'). |
| description | Description | Optional | Supplementary context for the title (e.g. 'Arrives in 4 business days', 'Arrives Dec 12-15 via FedEx'). Directly renderable; MUST NOT repeat the title. |
Availability¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| available | boolean | Optional | Whether this can be obtained. See status for fulfillment details. |
| status | string | Optional | Qualifies available with fulfillment state. Well-known values: in_stock, backorder, preorder, out_of_stock, discontinued. |
Fulfillment Destination Filter¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| address_country | string | Optional | The country, as a 2-letter ISO 3166-1 alpha-2 code (e.g. "US"). A 3-letter alpha-3 code or full country name MAY also be used. |
| address_region | string | Optional | The first-level administrative region within the country (e.g. a state or province such as California). |
| postal_code | string | Optional | The postal code (e.g. "94043"). |
| location | string | Optional | A reference to the destination (e.g. store, pickup location, saved address). |
Location and method: context and filters¶
contextcarries non-binding hints the Business uses to reportavailability: coarse locality fields for where the Buyer is, andlocationfor a provisional Business Location (see Location Context). On a market-scoped Catalog a Business MAY narrow results with these hints; otherwise they annotate results rather than remove them.filters.fulfills_tois where items are fulfilled to — a single destination (Fulfillment Destination Filter), named by value (a coarse locality) or by reference (alocationid — a store, pickup point, or saved address). Platforms SHOULD provide one or the other, not both; if both are present, a business SHOULD use the more specific — typicallylocation. It restricts results to what can be fulfilled there and seeds methodavailability, and may differ fromcontext(e.g. a gift).filters.methodsrestricts results to specific method types (e.g.["pickup"]).
context only hints; fulfills_to names the destination. See
Location Context for the precedence rule.
Example¶
A variant exposes two fulfillment methods: shipping to the buyer's ship-to
and pickup today at a named store. Each method carries its own availability,
and pickup references the resolved location by id.
{
"ucp": { "version": "2026-08-25" },
"products": [
{
"id": "prod_kettle",
"title": "Electric Kettle",
"description": { "plain": "1.7L electric kettle." },
"price_range": {
"min": { "amount": 4999, "currency": "USD" },
"max": { "amount": 4999, "currency": "USD" }
},
"variants": [
{
"id": "var_ss",
"title": "Stainless Steel",
"description": { "plain": "Stainless steel finish." },
"price": { "amount": 4999, "currency": "USD" },
"availability": { "available": true, "status": "in_stock" },
"fulfillment": {
"methods": [
{
"type": "shipping",
"description": { "plain": "Ships to your address in 1–4 business days" },
"availability": { "available": true, "status": "in_stock" },
"options": [
{
"id": "std",
"title": "Standard",
"description": { "plain": "Arrives in 4 business days" }
},
{
"id": "exp",
"title": "Express",
"description": { "plain": "Next business day" }
}
]
},
{
"type": "pickup",
"description": { "plain": "Pickup today at Downtown Store" },
"location": "loc_downtown",
"availability": { "available": true, "status": "in_stock" }
}
]
}
}
]
}
]
}
Each method is a way the variant can be fulfilled, with its own
availability. Each method's description is directly renderable, so a
platform can present it without recognizing the type (see
Rendering). The shipping method's description previews the
delivery range, and its options[] refine it (Standard, Express); pickup
carries none — options is optional.
Configuration¶
Businesses and platforms declare fulfillment constraints in their profiles. Businesses fetch platform profiles to adapt responses accordingly.
The extends array lists the capabilities this extension adds fulfillment
to. Checkout is the authoritative, transactional surface; catalog is for
discovery. A business lists the catalog capabilities in extends to expose
fulfillment on catalog, or omits them to scope itself to checkout only.
Platform Profile¶
Platforms declare their rendering capabilities using platform_schema:
| Name | Type | Requirement | Description |
|---|---|---|---|
| supports_multi_group | boolean | Optional | Enables multiple groups per method. |
Platforms that omit config or set supports_multi_group: false receive
single-group responses. The response shape is always
methods[].groups[]—the difference is whether groups.length can exceed 1
within each method.
Default declaration (single group per method; fulfillment surfaced on checkout and on catalog discovery):
{
"dev.ucp.shopping.fulfillment": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/extensions/fulfillment",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/fulfillment.json",
"extends": [
"dev.ucp.shopping.checkout",
"dev.ucp.shopping.catalog.search",
"dev.ucp.shopping.catalog.lookup"
]
}
]
}
A party that does not expose catalog discovery MAY narrow extends to
"dev.ucp.shopping.checkout" (string form) or to a single-element array.
Opt-in declaration (business MAY return multiple groups per method):
{
"dev.ucp.shopping.fulfillment": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/extensions/fulfillment",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/fulfillment.json",
"extends": [
"dev.ucp.shopping.checkout",
"dev.ucp.shopping.catalog.search",
"dev.ucp.shopping.catalog.lookup"
],
"config": { "supports_multi_group": true }
}
]
}
Business Profile¶
Businesses declare what fulfillment configurations they support using
business_config:
| Name | Type | Requirement | Description |
|---|---|---|---|
| multi_destination | Array[object] | Optional | Method types that permit multiple destinations within one cart (e.g. split shipping across addresses). Listing a method permits it; an omitted method does not. Open — businesses MAY list any method type. |
| method_combinations | Array[array] | Optional | Method-type combinations the business permits within one cart. Each inner array is a permitted set of method type values (e.g. shipping + pickup). |
{
"dev.ucp.shopping.fulfillment": [
{
"version": "2026-08-25",
"spec": "https://ucp.dev/2026-08-25/specification/shopping/extensions/fulfillment",
"schema": "https://ucp.dev/2026-08-25/schemas/shopping/fulfillment.json",
"extends": [
"dev.ucp.shopping.checkout",
"dev.ucp.shopping.catalog.search",
"dev.ucp.shopping.catalog.lookup"
],
"config": {
"multi_destination": [
{ "method": "shipping" }
],
"method_combinations": [["shipping", "pickup"]]
}
}
]
}
This example says: shipping can go to multiple addresses, and carts can mix shipping+pickup.
Business Response Behavior¶
When supports_multi_group: false (default):
- Business MUST consolidate all items into a single group per method
- Response still uses array structure:
methods[].groups[]withgroups.length === 1 - Business MAY still return multiple methods (e.g., shipping + pickup) if cart items require it
When supports_multi_group: true:
- Business MAY return multiple groups per method based on inventory, packaging, or warehouse logic
- Platform is responsible for rendering group selection UI (e.g., choose shipping speed per package)
Method Types¶
fulfillment_method.type (checkout) and catalog_fulfillment_method.type
(catalog) share one open-string vocabulary. Presentation is method-agnostic:
platforms SHOULD present every method, rendering description and
availability regardless of its type (see Rendering), and
SHOULD NOT omit a method solely because they do not recognize its type.
Recognizing a type only enables optional type-specific UX.
A method is identified by its type and its fulfillment scope (what it
fulfills and where). A business SHOULD model same-scope variation (e.g.
Standard vs Express) as options, and SHOULD NOT emit multiple methods
that differ only in option-level detail. Same-type methods are valid when
their scope differs — e.g. checkout may carry two shipping methods to
different destinations. In catalog a method covers a single variant at one
resolved location, so this collapses to at most one method per type.
Well-known values:
| Value | Meaning |
|---|---|
shipping |
Carrier ships to the buyer's address. |
pickup |
Buyer picks up at a named location. |
curbside |
Buyer picks up at a location without leaving their vehicle (drive-up). |
Adding method types. Because type is an open string, a business MAY
introduce a new value at any time with no consumer change: it advertises the
value (and filters on it via filters.methods), and consumers present it
like any other method.
Example — adding home_installation. No schema change or registration is
needed. Emit the value directly as the type on catalog and checkout, and
filter with filters.methods: ["home_installation"]. For checkout
negotiation, declare its behavior in the business profile config — e.g.
include ["shipping", "home_installation"] in method_combinations
so a cart can mix shipped and installed items (see
Business Profile). On a catalog variant's method:
{
"type": "home_installation",
"description": { "plain": "Delivered and installed in your home" },
"availability": {
"available": true
}
}
Examples¶
Basic¶
Config: None required (default behavior)
{
"ucp": { ... },
"id": "...",
"status": "...",
"currency": "...",
"line_items": [ ... ],
"totals": [ ... ],
"links": [ ... ],
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"selected_destination_id": "dest_1",
"destinations": [
{
"type": "shipping_address",
"id": "dest_1",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt", "pants"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard Shipping",
"description": { "plain": "Arrives Dec 12-15 via USPS" },
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express Shipping",
"description": { "plain": "Arrives Dec 10-11 via FedEx" },
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
}
]
}
}
Split Groups¶
Config: Platform profile requires config.supports_multi_group: true
Business splits items into multiple packages; buyer selects shipping rate per package.
{
"ucp": { ... },
"id": "...",
"status": "...",
"currency": "...",
"line_items": [ ... ],
"totals": [ ... ],
"links": [ ... ],
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"selected_destination_id": "dest_1",
"destinations": [
{
"type": "shipping_address",
"id": "dest_1",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [ {"type": "total", "amount": 500} ]
},
{
"id": "express",
"title": "Express",
"totals": [ {"type": "total", "amount": 1000} ]
}
]
},
{
"id": "package_2",
"line_item_ids": ["pants"],
"selected_option_id": "express",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [ {"type": "total", "amount": 500} ]
},
{
"id": "express",
"title": "Express",
"totals": [ {"type": "total", "amount": 1000} ]
}
]
}
]
}
]
}
}
Split Destinations¶
Config: Business profile lists shipping in config.multi_destination
Shirt ships to mom (US), pants ship to grandma (Hong Kong). Two methods of the same type, each with its own destination.
{
"ucp": { ... },
"id": "...",
"status": "...",
"currency": "...",
"line_items": [ ... ],
"totals": [ ... ],
"links": [ ... ],
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt"],
"selected_destination_id": "dest_mom",
"destinations": [
{
"type": "shipping_address",
"id": "dest_mom",
"street_address": "123 Mom St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express",
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
},
{
"id": "method_2",
"type": "shipping",
"line_item_ids": ["pants"],
"selected_destination_id": "dest_grandma",
"destinations": [
{
"type": "shipping_address",
"id": "dest_grandma",
"street_address": "88 Queensway",
"address_locality": "Hong Kong",
"address_country": "HK"
}
],
"groups": [
{
"id": "package_2",
"line_item_ids": ["pants"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express",
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
}
]
}
}