# Track orders

Find the shopper's orders and shipments with search_orders, and read their status, tracking and totals without reading more into them than they say.

`search_orders` searches the order history Shop keeps for the signed-in shopper. That includes orders from Shopify stores, and purchases and shipments that Shop found in an email inbox the shopper connected to it. The tool needs [sign-in](sign-in.md).

## Search orders

Every argument is optional. With none, it returns the most recent orders:

```json
{ "query": "running shoes", "activity": "active", "pagination": { "limit": 10 } }
```

| Argument | Value |
| --- | --- |
| `query` | Words to match against the merchant, the items or the order number, up to 200 characters |
| `activity` | `active` for orders that haven't been delivered, `past` for delivered or closed ones, or `all`, the default |
| `date_from`, `date_to` | The first and last order dates to include, as `YYYY-MM-DD` |
| `pagination` | `limit` takes 1 to 25 and defaults to 10. To get the next page, pass back the returned `cursor` unchanged. |

Search with what the shopper tells you, such as a store or an item. They don't need to find an order number first.

From the CLI:

```sh
shop order search 'running shoes' --activity active --limit 10
```

Fetch only the pages you need to answer the question. Shop limits how often each shopper can search through each client, currently to 10 searches a minute, 40 an hour, 100 a day and 200 a week. If a search comes back `rate_limited`, don't repeat it in a loop.

## Read the results

The result has `orders`, `shipments` and `pagination`. This order is shortened, with made-up values:

```json
{
  "orders": [
    {
      "id": "<order id>",
      "label": "#2042",
      "placed_at": "2026-09-20T12:00:00Z",
      "source": "shopify",
      "seller": { "name": "Example Running Co.", "domain": "running.example" },
      "status": "delivered",
      "currency": "USD",
      "totals": [{ "type": "total", "amount": 12000 }],
      "line_items": [
        {
          "id": "<line id>",
          "item": { "title": "Trail shoes", "price": 12000 },
          "quantity": { "original": 1, "total": 1, "fulfilled": 1 },
          "status": "fulfilled"
        }
      ],
      "fulfillment": {
        "events": [
          {
            "type": "delivered",
            "status": "DELIVERED",
            "occurred_at": "2026-09-22T12:00:00Z",
            "carrier": "Example Post",
            "tracking_number": "<tracking number>",
            "tracking_url": "https://carrier.example/track/<tracking number>"
          }
        ]
      },
      "permalink_url": "<order page URL>"
    }
  ],
  "shipments": [],
  "pagination": { "has_next_page": false }
}
```

| Field | What it holds |
| --- | --- |
| `label` | The order number the merchant shows, such as `#2042` |
| `source` | `shopify` for an order from a Shopify store, or `email` for a purchase Shop found in the shopper's inbox |
| `status`, `delivery_status`, `payment_status` | Shop's statuses for the order, as Shop reports them. Orders found by email have no `payment_status`. |
| `line_items` | Each item, with its unit `price` and a `quantity` made of `original`, the count at checkout, `total`, the count still active after returns or cancellations, and `fulfilled` |
| `totals` | The order total, and a subtotal when Shop has one |
| `fulfillment.events` | The latest state of each shipment, with its carrier and tracking |
| `fulfillment.expectations` | Delivery estimates |
| `adjustments` | Refunds, returns and other changes after the order was placed |
| `permalink_url` | The shopper's page for the order |
| `return_url` | The merchant's page for starting a return, when it has one |

`shipments` lists parcels Shop is tracking that don't belong to an order it knows about, usually found by email. Each has a `label`, `seller_name`, `state` and a delivery estimate.

### Amounts

Amounts are integers in minor units of the order's `currency`. A currency of `XXX` means Shop doesn't know it. Those amounts still count hundredths, so show `1250` as 12.50 with no currency symbol. Never add amounts in different or unknown currencies into one total.

### Shipping status

Each event in `fulfillment.events` is one shipment's latest known state, not its full history. Its `status` is Shop's own delivery status, such as `LABEL_CREATED` or `DELIVERED`. `type` maps it to a broader UCP value such as `in_transit` or `delivered`, and several statuses share one type, so use `status` and the carrier's tracking for detail.

A `LABEL_CREATED` status only says a shipping label exists. A line marked `fulfilled` hasn't necessarily shipped either. Neither one means the parcel is on its way or has arrived.

### Missing fields

A missing field means Shop doesn't know, not that the order is unpaid or undelivered. Orders found by email can lack prices, tracking and links, and lines without a price come back in `unpriced_line_items`. Even an order from a Shopify store can lack `permalink_url`. Don't build a link yourself, or show an order ID in its place.

If `quantity.original` is missing, `quantity.total` still gives the count that's active now, but not how many the shopper bought.

## Buy something again

Don't take an ID from an order line and check out with it. Find the item in the catalog with `search_catalog` or `lookup_catalog`, check its current options, stock and price with the shopper, then create a checkout as usual.

## What isn't supported

Shop MCP can't look up an order at the merchant, start a return or cancel an order. When an order has a `return_url` or `permalink_url`, give it to the shopper for those.
