Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b991c7d
Extend retail_location.json with more fields and move it out of shopp…
jingyli Jun 3, 2026
c5ad8d2
Add appropriate transition annotation to set proper expectation on fi…
jingyli Jun 3, 2026
525843e
Add common location capability.
jingyli Jun 24, 2026
fe04eaa
Minor wording updates in documentation and also fix rendering issues.
jingyli Jun 25, 2026
111ea07
Minor fix on fulfillment retail location reference.
jingyli Jun 25, 2026
a14bfea
Fix on rendering links.
jingyli Jun 25, 2026
43f7df5
More fixes on rendering links.
jingyli Jun 25, 2026
3269387
Fix location_filter file name.
jingyli Jun 29, 2026
bb67299
Clean up remaining broken links and minor style updates.
jingyli Jun 29, 2026
8a3ec67
Address comments that involve minor schema changes.
jingyli Jul 15, 2026
c12499f
Address rest of the comments from feedback.
jingyli Jul 15, 2026
80cb4ee
Fix broken doc build link.
jingyli Jul 15, 2026
3c8a694
fix(location): mark custom filters as extensible
igrigorik Jul 16, 2026
310b645
docs(location): remove catalog copy-paste
igrigorik Jul 16, 2026
727bf04
Merge branch 'main' into feat/location
jingyli Jul 30, 2026
5e3ff05
Add location into UCP glossary to resolve feedback on PR#642.
jingyli Jul 30, 2026
1bd596e
Add security & privacy considerations.
jingyli Jul 30, 2026
e5a0cdd
Address feedback on simplifying service area representation on locati…
jingyli Jul 30, 2026
c4087e2
Update hours representation to be more consistent with standard schem…
jingyli Jul 30, 2026
e4d2189
Relax exact quantity based search and instead leverage availability s…
jingyli Jul 30, 2026
918c1cb
Standardize amenities vocabulary and also restructure filtering model…
jingyli Jul 30, 2026
1ff81f3
Address bounded location representation problem and clean up misc/unu…
jingyli Jul 30, 2026
0dc7e06
Fix broken reference rendering.
jingyli Jul 30, 2026
d4f5f4b
Merge branch 'main' into feat/location
jingyli Jul 30, 2026
80145dd
Fix example on REST to follow proper exception_hour representation.
jingyli Jul 31, 2026
675ec06
Clean up legacy field description.
jingyli Aug 1, 2026
17dac60
Fix signature definition in MCP JSONRPC transport schema definition.
jingyli Aug 1, 2026
5dfcbf7
Add validation rule for timezone in location.json.
jingyli Aug 1, 2026
07f2051
Loosen language on what is being considered as a search inputs to giv…
jingyli Aug 1, 2026
41a47e6
Add some implementation guidance on how to deal with context-only sea…
jingyli Aug 1, 2026
054a763
Add more validation on the filter schema and tighten up prose around …
jingyli Aug 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .cspell/custom-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,20 @@ punycode
userinfo
examplecorp
zapatillas
jwks
keyid
reauth
redeemables
reprepare
sandboxing
fbclid
gclid
ttclid
Accor
Kogan
Petbarn
VTEX
geofence
geofencing
wifi
optionalities
2 changes: 1 addition & 1 deletion docs/specification/cart-rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ All REST endpoints **MUST** be served over HTTPS with minimum TLS version 1.3.
The following headers are defined for the HTTP binding and apply to all
operations unless otherwise noted.

{{ header_fields('create_cart', 'rest.openapi.json') }}
{{ header_fields('create_cart', 'shopping/rest.openapi.json') }}

### Specific Header Requirements

Expand Down
8 changes: 4 additions & 4 deletions docs/specification/catalog/rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Businesses advertise REST transport availability through their UCP profile at

Maps to the [Catalog Search](search.md) capability.

{{ method_fields('search_catalog', 'rest.openapi.json', 'catalog/rest') }}
{{ method_fields('search_catalog', 'shopping/rest.openapi.json', 'catalog/rest') }}

#### Example

Expand Down Expand Up @@ -201,7 +201,7 @@ for supported identifiers, resolution behavior, and client correlation requireme
The request body contains an array of identifiers and optional context that
applies to all lookups in the batch.

{{ method_fields('lookup_catalog', 'rest.openapi.json', 'catalog/rest') }}
{{ method_fields('lookup_catalog', 'shopping/rest.openapi.json', 'catalog/rest') }}

#### Example: Batch Lookup with Context

Expand Down Expand Up @@ -353,7 +353,7 @@ messages indicating which identifiers were not found.
Maps to the [Catalog Lookup](lookup.md#get-product-get_product) capability. Returns a singular
`product` object (not an array) for full product detail page rendering.

{{ method_fields('get_product', 'rest.openapi.json', 'catalog/rest') }}
{{ method_fields('get_product', 'shopping/rest.openapi.json', 'catalog/rest') }}

#### Example: With Option Selection

Expand Down Expand Up @@ -514,7 +514,7 @@ with the same identifier.
The following headers are defined for the HTTP binding and apply to all
operations unless otherwise noted.

{{ header_fields('search_catalog', 'rest.openapi.json') }}
{{ header_fields('search_catalog', 'shopping/rest.openapi.json') }}

### Specific Header Requirements

Expand Down
2 changes: 1 addition & 1 deletion docs/specification/checkout-rest.md
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@ for status and `order` semantics.
The following headers are defined for the HTTP binding and apply to all
operations unless otherwise noted.

{{ header_fields('create_checkout', 'rest.openapi.json') }}
{{ header_fields('create_checkout', 'shopping/rest.openapi.json') }}

### Specific Header Requirements

Expand Down
10 changes: 5 additions & 5 deletions docs/specification/checkout.md
Original file line number Diff line number Diff line change
Expand Up @@ -790,7 +790,7 @@ should accept an additional `cart_id` field for cart-to-checkout conversion. See
[Cart → Cart-to-Checkout Conversion](cart.md#cart-to-checkout-conversion) for
the field contract.

{{ method_fields('create_checkout', 'rest.openapi.json', 'checkout') }}
{{ method_fields('create_checkout', 'shopping/rest.openapi.json', 'checkout') }}

### Get Checkout

Expand All @@ -803,7 +803,7 @@ checkout.
The platform will honor the TTL provided by the business via `expires_at` at the
time of checkout session creation.

{{ method_fields('get_checkout', 'rest.openapi.json', 'checkout') }}
{{ method_fields('get_checkout', 'shopping/rest.openapi.json', 'checkout') }}

### Update Checkout

Expand All @@ -815,7 +815,7 @@ general replacement rule does not apply during `complete_in_progress` because
Update Checkout is not permitted; see
[Accepted completion](#accepted-completion) for the frozen operation contract.

{{ method_fields('update_checkout', 'rest.openapi.json', 'checkout') }}
{{ method_fields('update_checkout', 'shopping/rest.openapi.json', 'checkout') }}

### Complete Checkout

Expand Down Expand Up @@ -856,7 +856,7 @@ to construct the order representation (i.e. information like `line_items`,
After the order is placed, other details will be updated through subsequent
events as the order, and its associated items, move through the supply chain.

{{ method_fields('complete_checkout', 'rest.openapi.json', 'checkout') }}
{{ method_fields('complete_checkout', 'shopping/rest.openapi.json', 'checkout') }}

### Cancel Checkout

Expand All @@ -866,7 +866,7 @@ already canceled or completed), then businesses **SHOULD** send back an error
indicating the operation is not allowed. Any checkout session with a status
that is not equal to `completed` or `canceled` **SHOULD** be cancelable.

{{ method_fields('cancel_checkout', 'rest.openapi.json', 'checkout') }}
{{ method_fields('cancel_checkout', 'shopping/rest.openapi.json', 'checkout') }}

## Transport Bindings

Expand Down
4 changes: 2 additions & 2 deletions docs/specification/fulfillment.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ method.

{{ schema_fields('types/shipping_destination_resp', 'fulfillment') }}

#### Retail Location
#### Location

{{ schema_fields('types/retail_location_resp', 'fulfillment') }}
{{ schema_fields('types/location_resp', 'fulfillment') }}

#### Fulfillment Group

Expand Down
13 changes: 7 additions & 6 deletions docs/specification/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ acronym in each specification Markdown file spells out the full term (e.g.,

## Commerce

| Term | Acronym | Definition |
| :--------------------------- | :------ | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Business** | - | The entity selling goods or services. In UCP, they act as the **Merchant of Record (MoR)**, retaining financial liability and ownership of the order. |
| **Merchant of Record** | MoR | The legal entity responsible for the sale, including financial liability and order ownership. |
| **Payment Service Provider** | PSP | The financial infrastructure provider that processes payments, authorizations, and settlements on behalf of the business. |
| **Platform** | - | The consumer-facing surface (AI agent, app, website) acting on behalf of the user to discover businesses and facilitate commerce. |
| Term | Acronym | Definition |
| :--------------------------- | :------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Business** | - | The entity selling goods or services. In UCP, they act as the **Merchant of Record (MoR)**, retaining financial liability and ownership of the order. |
| **Merchant of Record** | MoR | The legal entity responsible for the sale, including financial liability and order ownership. |
| **Payment Service Provider** | PSP | The financial infrastructure provider that processes payments, authorizations, and settlements on behalf of the business. |
| **Platform** | - | The consumer-facing surface (AI agent, app, website) acting on behalf of the user to discover businesses and facilitate commerce. |
| **Location** | - | A physical entity that can be found on a map. Identified by a stable, business-scoped string (id) and referenced across UCP for user contextualization and location discovery.|

## Payments

Expand Down
159 changes: 159 additions & 0 deletions docs/specification/location/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<!--
Copyright 2026 UCP Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# Location Capability

The Location capability allows platforms to discover, search, and retrieve physical locations
(such as retail stores, restaurants, brand lockers) from businesses.

This is vertical-agnostic and enables key commerce flows such as:

* **Local Pickup Discovery**: Finding locations like retail stores or restaurant branches
nearby that support customer pickup and checking their operating hours & inventory availability
before selection.
* **Fulfillment Area Verification**: Checking if a specific location (e.g., utility depot, restaurant,
or local service provider) has delivery coverage for a buyer's address.

## Capabilities

| Capability | Description |
| :--- | :--- |
| [`dev.ucp.common.location.search`](search.md) | Search for locations using natural language queries and filters (hours, offerings, geofencing). |
| [`dev.ucp.common.location.lookup`](lookup.md) | Retrieve full details for one or more locations by identifier. |

## Key Concepts

* **Location**: A physical entity that can be found on a map. Defined by a display name,
address, operating hours, and **geographic context** (geographic coordinates).
* **Offerings**: Features, capabilities, and inventory provided by the location.
This is split into two distinct concepts to ensure tooling compatibility and semantic clarity:
* **Amenities**: Static features, services, or capabilities of the location
(e.g., `free_wifi`, `parking`, `outdoor_seating`, `curbside_pickup`).
* **Inventory**: Dynamic availability of goods (e.g., retail products or restaurant dishes).
* **Geofencing & Service Area**: Locations implicitly carry a geofence boundary around their coordinates.
This is used to determine if a location can serve a specific user (e.g., delivery area check).
Clients can perform proximity searches (`distance` filter) or coverage checks (`geofence_point` filter)
using the filter.
* **Operating Hours**: Weekly schedules (`hours`) and date-specific overrides
(`exception_hours` - e.g., holidays, temporary closures) associated with a timezone.

### Relationship to Other Capabilities

The Location capability provides the foundation for localized commerce by integrating tightly
other capabilities (like Catalog, Cart, and Checkout in Shopping):

1. **Stable Identifiers**: Location search/lookup operations return stable,
business-scoped `location.id` values. These IDs are referenced further in other requests & responses
(e.g., associating product variants to specific locations in Catalog filters, passed directly
in `selected_destination_id` to indicate pickup fulfillment mode).
2. **Inventory-Based Store Finder**: Platforms can use Location Search with the `offerings.inventory` filter
to locate nearby stores that have a specific item available, bridging the gap between online catalog
browsing and physical store visits.
3. **Provisional vs. Authoritative Boundaries**:
* *Discovery Phase (Provisional)*: Location responses based on operating hours, real-time inventory
availability, and amenities offerings represent the business's *current terms* at the
time of query. They are **provisional signals** (despite most, like hours & amenities, remain stable
overtime) and are not binding commitments.
* *Checkout Phase (Authoritative)*: Final transaction terms that depend on a location (e.g., pickup)
**MUST** be negotiated and finalized authoritatively. Discovery signals **SHOULD NOT** be cached
or reused across sessions without re-validation.

## Shared Entities

### Context

User location and market context for the operations. All fields are optional
hints for relevance and localization. Platforms **MAY** geo-detect context from
request headers.

Context signals are provisional—not authoritative data. Businesses **SHOULD** use
these values when verified inputs (e.g., coordinates as part of the request filter)
are absent, and **MAY** ignore or down-rank them if inconsistent with
higher-confidence signals (authenticated account, risk detection).

{{ schema_fields('types/context', 'location') }}

### Signals

Environment data provided by the platform to support authorization
and abuse prevention. Signal values **MUST NOT** be buyer-asserted claims. See
[Signals](../overview.md#signals) for details and privacy requirements.

{{ schema_fields('types/signals', 'location') }}

## Messages and Error Handling

All location responses include an optional `messages` array that allows businesses
to provide context about errors, warnings, or informational notices.

### Message Types

Messages communicate business outcomes and provide context:

| Type | When to Use | Example Codes |
| :--- | :--- | :--- |
| `error` | Business-level errors | `no_service_coverage` (geographic coordinates based filter) |
| `warning` | Important conditions affecting purchase | `permanently_closed`, `temporary_closure` |
| `info` | Additional context without issues | `not_found`, `holiday_hours_active` |

#### Message (Error)

{{ schema_fields('types/message_error', 'location') }}

#### Message (Warning)

{{ schema_fields('types/message_warning', 'location') }}

#### Message (Info)

{{ schema_fields('types/message_info', 'location') }}

### Common Scenarios

#### Empty Search

When search finds no matches, return an empty array without messages.

<!-- ucp:example schema=common/location_search op=search -->
```json
{
"ucp": {...},
"locations": []
}
```

This is not an error - the query was valid but returned no results.

## Transport Bindings

The capabilities above are bound to specific transport protocols:

* [REST Binding](rest.md): RESTful API mapping.
* [MCP Binding](mcp.md): Model Context Protocol mapping via JSON-RPC.

## Security & Privacy Considerations

1. **Coarse-by-default**: Platforms **SHOULD** default to sending coarse location hints (e.g., postal code or rounded coordinates) during the discovery phase.
Precise locations/coordinates **SHOULD** only be shared when the user explicitly consents or selects a specific location.
2. **Inventory Probing Mitigation**: Businesses **SHOULD** implement rate-limiting on search requests, especially if containing inventory availability filters,
to prevent scraping & aggressive numeration of the entire directory.
3. **Private/Dark Locations**: Businesses **MUST** filter out internal-only or non-user-accessible locations (e.g., dark kitchens, fulfillment-only hubs)
from search results.
4. **Physical Address Spoofing (Integrity)**: While location discovery is read-only, tampering with physical addresses in responses (e.g., through MITM attacks)
poses a physical safety/fraud risk. Platforms **SHOULD** verify signatures on location payloads before rendering them to users.
5. **Data Retention & Logging Sanitization**: Businesses **MUST NOT** persist precise location inputs beyond the lifecycle of the request, unless explicit user
consent is collected. Server logs should sanitize coordinate inputs by truncating decimal places (e.g., to 2 decimal places, ~1km accuracy) to prevent
accidental storage of precise user history.
76 changes: 76 additions & 0 deletions docs/specification/location/lookup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!--
Copyright 2026 UCP Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# Location Lookup Capability

* **Capability Name:** `dev.ucp.common.location.lookup`

Retrieves physical locations by their unique identifiers.
Supports full-detail batch retrieval of multiple locations to provide optionalities
or retrieval of a single location (useful for a dedicated location detail page).

## Operation

| Operation | Description |
| :--------------------- | :-------------------------------------------- |
| **Lookup Location(s)** | Retrieve single or multiple locations by ID. |

## Supported Identifiers

The `ids` parameter accepts an array of identifiers. Implementations **MUST**
support lookup by the business's stable location ID.

Duplicate identifiers in the request **MUST** be deduplicated by the server.
When multiple identifiers resolve to the same physical location,
it **MUST** be returned only once in the response.

### Client Correlation

The response does not guarantee order. Clients correlate returned locations
simply by matching the returned `id` field against their requested `ids`.

### Batch Size

Implementations **SHOULD** accept at least 10 identifiers per request.
Implementations **MAY** enforce a maximum batch size and **MUST** reject
requests exceeding their limit with an appropriate error (HTTP 400
`request_too_large` for REST, JSON-RPC `-32602` for MCP).

### Filters

Optional `filters` (hours, offerings/inventory, geo) are accepted
to narrow down the returned locations.
Filters use the same schema and AND semantics as [Search Filters](search.md#search-filters).

Filters apply **after** identifier resolution. For example, if a client requests
`["loc_downtown", "loc_uptown"]` with a filter of `open_now: true`:

1. The server first resolves both identifiers to their respective locations.
2. The server then evaluates the `open_now` hour filter against each resolved location.
3. If `loc_uptown` is currently closed, it is excluded, and only `loc_downtown` is returned.

### Request

{{ extension_schema_fields('location_lookup.json#/$defs/lookup_request', 'location') }}

### Response

{{ extension_schema_fields('location_lookup.json#/$defs/lookup_response', 'location') }}

## Transport Bindings

* [REST Binding](rest.md#post-locationslookup): `POST /locations/lookup`
* [MCP Binding](mcp.md#lookup_locations): `lookup_locations` tool
Loading
Loading