# Locations and collection

> Find where a shop's orders can be collected with UCP location search and lookup, and check out with collection at one of those places on every door.

Source: https://storeware.ai/docs/agents/pickup

A shop can let customers collect orders from its own locations. An agent can find those places, ask which of them hold the goods, and check out with collection at one of them. The shop's own checkout offers exactly the same choice, so a basket an agent builds and a basket a shopper builds see the same options.

## Switching it on

Collection is set per location in the admin, under **Locations**. Open a location, give it an address, and tick **Customers can collect from here**. The same screen takes instructions for customers (where to go, what to bring), the opening hours for collection, and how long an order usually takes to be ready.

Only the shop's own locations can offer collection. A supplier's warehouse cannot, and neither can a location with no address. Collection is always free.

## Finding a location

The two capabilities are the ones the pinned UCP release defines, `dev.ucp.common.location.search` and `dev.ucp.common.location.lookup`. They belong to the `dev.ucp.common` service, which the shop's profile declares on the same endpoints as shopping.

| Transport       | Search                                    | Lookup                                    |
| --------------- | ----------------------------------------- | ----------------------------------------- |
| UCP REST        | `POST /api/ucp/v1/locations/search`       | `POST /api/ucp/v1/locations/lookup`       |
| UCP MCP and A2A | `search_locations`, request in `location` | `lookup_locations`, request in `location` |

Any agent key that may browse can ask. Only locations that offer collection are ever listed. A shop with none answers with an empty list.

```json
{
  "query": "Leeds",
  "serves": { "address": { "address_country": "GB" } },
  "filters": {
    "items": ["6f1c..."],
    "hours": { "open_at": "2026-11-14T10:00:00Z" }
  }
}
```

| Field               | What it does                                                                                                 |
| ------------------- | ------------------------------------------------------------------------------------------------------------ |
| `query`             | Orders the answer by the words a location's name and address carry. It never leaves a location out.          |
| `serves.address`    | Keeps locations in a country (two letters), a region, or a postcode. `LS1` matches `LS1 1AA`.                |
| `filters.items`     | Keeps locations that hold at least one of every variant named, or keep selling it once sold out.             |
| `filters.hours`     | Keeps locations open at that instant, read in the shop's time zone. A location with no hours does not match. |
| `filters.amenities` | Every location has `dev.ucp.amenity.shopping.in_store_pickup` and nothing else.                              |
| `pagination`        | Ten to a page unless `limit` says otherwise, fifty at most, with `total_count`.                              |

Each location carries its `id`, `name`, `address`, the in-store pickup amenity with how long an order usually takes and the shop's instructions, and `hours` with `timezone` when the shop gave hours. The address never includes the phone number or email the shop gave its carrier.

### Distance is not supported

Locations here have no coordinates, so there is no `geo`, and a request with `distance` or `serves.point` is refused with `unsupported_predicate` rather than answered approximately. Ask by country, region or postcode instead.

## Checking out with collection

A checkout offers collection from every location whose shelf holds the whole basket. In UCP it is a method of type `pickup`, whose `destinations` are the locations as `business_location` entries:

```json
{
  "id": "pickup",
  "type": "pickup",
  "line_item_ids": ["li_1"],
  "destinations": [
    {
      "id": "6f1c...",
      "type": "business_location",
      "name": "Leeds shop",
      "address": { "...": "..." }
    }
  ],
  "selected_destination_id": null,
  "groups": [
    { "id": "basket", "options": [{ "id": "pickup:6f1c...", "title": "Collect from Leeds shop" }] }
  ]
}
```

Choose one by sending the location's id as `selected_destination_id` on the pickup method. The id from a location search works directly. Choosing the option in the group does the same. A location that cannot take this basket is refused with `invalid_destination`, and the checkout is not switched to another location.

Once collection is chosen, the basket needs no delivery address. Tax is worked out where the order is collected. Nothing is ever set to collection for the buyer: an agent or a shopper has to choose it.

The placed order carries one `pickup` expectation with the location's address, and the shop marks it ready to collect when it is.

On ACP, and on the shop's own MCP server, collection is a fulfilment option of type `pickup` with the location's name, address and instructions, chosen like any other option.

## Before a basket

A [delivery quote](https://storeware.ai/docs/agents/delivery-quotes) lists collection from the shop's locations in the destination's country, beside its delivery rates, with `type: "pickup"` and the location it is collected from.
