--- title: Order MCP server description: >- Use the Order MCP server to fetch the current state of an order placed through your agent, including line items, fulfillment events, and post-purchase adjustments. source_url: html: 'https://shopify.dev/docs/agents/orders/order-mcp' md: 'https://shopify.dev/docs/agents/orders/order-mcp.md' --- # Order MCP The Order MCP server enables AI agents to fetch the current state of an order placed through their agent, including line items, fulfillment events, and post-purchase adjustments. ## How it works Order MCP implements the UCP order capability (`dev.ucp.shopping.order`, version `2026-04-08`). The capability exposes a single tool, [`get_order`](#get_order), which returns the full current state of an order. For background, see [About orders](https://shopify.dev/docs/agents/orders). For the canonical specification, see the [Order capability](https://ucp.dev/2026-04-08/specification/order/) and the [Order MCP binding](https://ucp.dev/2026-04-08/specification/order-mcp/). **Info:** **Use webhooks as the primary update channel.** Webhooks deliver the full current order state whenever a committed change occurs. Use `get_order` for buyer-initiated views, reconciling dropped webhook deliveries, or any case where you need fresh state on demand. Don't poll `get_order` on a schedule. Subscribe to [Order webhooks](https://shopify.dev/docs/agents/orders/order-webhooks) instead. ### Rate limits Order MCP is available only to Token-tier agents. `get_order` requires a Global API JWT with the `read_global_api_orders` scope, which Signed-tier and Anonymous-tier agents can't obtain. The agent profile must also declare `dev.ucp.shopping.order` for the tool to appear on the negotiated session. Webhooks remain the primary update channel for the order capability. Reserve `get_order` for buyer-initiated reads and reconciliation. When the server rate-limits a request, retry after the delay specified by the HTTP `Retry-After` response header and apply exponential backoff with jitter. For tier definitions, capability matrices, and the full guidance, see [Auth and rate limiting](https://shopify.dev/docs/agents/profiles/auth-and-rate-limiting). | Use case | Recommended surface | | - | - | | Proactive notifications and timeline UIs | [Order webhooks](https://shopify.dev/docs/agents/orders/order-webhooks) | | Buyer-initiated "Where's my order?" views | `get_order` | | Reconciling missed webhook deliveries | `get_order` | | Scheduled polling | Not recommended. Process incoming order webhook deliveries instead. | ## Authenticate Order MCP requests require a Global API JWT with the `read_global_api_orders` scope. Obtain your client credentials from [the **Catalog** section of Dev Dashboard](https://shopify.dev/docs/apps/build/dev-dashboard) and exchange them for a token. The response contains: * `access_token`: A JWT used in the `Authorization: Bearer {token}` header on subsequent requests. * `scope`: The list of access scopes granted to the token. * `expires_in`: The number of seconds until the access token expires. Access tokens have a 60-minute TTL. Mint a fresh token rather than reusing one across long-lived sessions. You can only fetch orders that were placed through your agent. For details on traffic tiers and what each can do, see [Auth and rate limiting](https://shopify.dev/docs/agents/profiles/auth-and-rate-limiting). POST ## https://api.shopify.com/auth/access\_token ```bash curl --request POST \ --url https://api.shopify.com/auth/access_token \ --header 'Content-Type: application/json' \ --data '{ "client_id": "{your_client_id}", "client_secret": "{your_client_secret}", "grant_type": "client_credentials" }' ``` ## {} Response ```json { "access_token": "f8563253df0bf277ec9ac6f649fc3f17", "scope": "read_global_api_orders", "expires_in": 3599 } ``` ## Make requests Order MCP shares the merchant's UCP MCP endpoint with [Cart MCP](https://shopify.dev/docs/agents/carts-and-checkout/cart-mcp) and [Checkout MCP](https://shopify.dev/docs/agents/carts-and-checkout/checkout-mcp). Send `POST` requests to `https://{shop-domain}/api/ucp/mcp` using the JSON-RPC 2.0 protocol. Every request must include a `meta` object in `arguments`. Include `meta["ucp-agent"]` with a `profile` URI (your agent's [UCP profile at `/.well-known/ucp`](https://ucp.dev/2026-04-08/specification/overview)) for [capability negotiation](https://shopify.dev/docs/agents/profiles). The order is returned in `result.structuredContent`. The `result.content` array also includes a stringified text representation of the order. For testing, you can reference Shopify's hosted profile fixture at `https://shopify.dev/ucp/agent-profiles/2026-04-08/valid-with-capabilities.json`, which already declares the order capability. POST ## https://{shop-domain}/api/ucp/mcp ```json { "jsonrpc": "2.0", "method": "tools/call", "id": 1, "params": { "name": "get_order", "arguments": { "meta": { "ucp-agent": { "profile": "https://shopify.dev/ucp/agent-profiles/2026-04-08/valid-with-capabilities.json" } }, "id": "gid://shopify/Order/6252199051413" } } } ``` ## Order tools **Caution:** Tool use is subject to access and rate limiting that scale with how your agent identifies itself. See [Auth and rate limiting](https://shopify.dev/docs/agents/profiles/auth-and-rate-limiting) for traffic tiers and what each can do. Order MCP provides one tool for retrieving the current state of an order. * [`get_order`](#get_order): Fetch the current state of an order placed through your agent. ### `get_order` Returns the current-state snapshot of an order placed through your agent. The order ID must be a Shopify Global ID returned by [`complete_checkout`](https://shopify.dev/docs/agents/carts-and-checkout/checkout-mcp#complete_checkout) on a checkout your agent originated. Orders placed through other apps or directly on the merchant's storefront aren't accessible, and `get_order` can't look up cross-channel buyer order history. Buyer identity linking isn't supported in v1. **Info:** **Allow propagation time after `complete_checkout`.** After `complete_checkout` returns, allow about 10 seconds before calling `get_order` for the first time. The order needs time to propagate to the order service. When to use: * The buyer asks "Where's my order?" or "What's the status of order #1042?" * You need to reconcile state after a missed webhook delivery. * You need fresh data on demand for a buyer-facing UI. #### Parameters meta•objectRequired (critical) Request metadata. You're required to include `ucp-agent.profile`, the URI of your agent's UCP profile for capability negotiation. The profile must declare `dev.ucp.shopping.order` for `get_order` to be available in the negotiated session. id•stringRequired (critical) The Shopify Global ID of the order. Format: `gid://shopify/Order/{id}`. POST ## https://{shop-domain}/api/ucp/mcp ```json { "jsonrpc": "2.0", "method": "tools/call", "id": 1, "params": { "name": "get_order", "arguments": { "meta": { "ucp-agent": { "profile": "https://shopify.dev/ucp/agent-profiles/2026-04-08/valid-with-capabilities.json" } }, "id": "gid://shopify/Order/6252199051413" } } } ``` ## {} Response ```json { "jsonrpc": "2.0", "id": 1, "result": { "isError": false, "structuredContent": { "ucp": { "version": "2026-04-08", "capabilities": { "dev.ucp.shopping.order": [{ "version": "2026-04-08" }] } }, "id": "gid://shopify/Order/6252199051413", "label": "#1042", "checkout_id": "gid://shopify/Checkout/7904f172582fa0de7a67f1839f8ed7ae?key=3d5fe2f0d03a5ecf432a17c6c4c1dde5", "permalink_url": "https://cool-store.myshopify.com/96686207971/orders/eaadd7a71f2656ef2684275972df05da/authenticate", "currency": "USD", "totals": [ { "type": "subtotal", "display_text": "subtotal", "amount": 6298 }, { "type": "fulfillment", "display_text": "fulfillment", "amount": 899 }, { "type": "tax", "display_text": "tax", "amount": 780 }, { "type": "fee", "display_text": "fee", "amount": 0 }, { "type": "total", "display_text": "total", "amount": 7977 } ], "line_items": [ { "id": "gid://shopify/LineItem/14584740544661", "item": { "id": "gid://shopify/ProductVariant/46040179376277", "title": "Large / Black", "price": 3999, "image_url": "https://cdn.shopify.com/s/files/1/2637/1970/products/classic-tee.jpg" }, "quantity": { "original": 2, "total": 1, "fulfilled": 1 }, "totals": [ { "type": "subtotal", "display_text": "subtotal", "amount": 3999 }, { "type": "tax", "display_text": "tax", "amount": 520 }, { "type": "total", "display_text": "total", "amount": 4519 } ], "status": "fulfilled", "parent_id": null } ], "fulfillment": { "expectations": [ { "id": "gid://shopify/Expectation/6252199051413", "line_items": [{ "id": "gid://shopify/LineItem/14584740544661", "quantity": 1 }], "method_type": "shipping", "destination": { "first_name": "Jane", "last_name": "Smith", "street_address": "123 Main Street", "address_locality": "Brooklyn", "address_region": "NY", "address_country": "US", "postal_code": "11201" } } ], "events": [ { "id": "gid://shopify/Fulfillment/5650582175893?status=created", "occurred_at": "2026-03-25T14:30:00-04:00", "type": "shipped", "description": null, "tracking_number": "1Z999AA10123456784", "tracking_url": "https://www.ups.com/WebTracking?trackNums=1Z999AA10123456784", "carrier": "UPS", "line_items": [{ "id": "gid://shopify/LineItem/14584740544661", "quantity": 1 }] }, { "id": "gid://shopify/FulfillmentEvent/8901234567", "occurred_at": "2026-03-26T09:15:00-04:00", "type": "delivered", "description": "Delivered to front door", "tracking_number": "1Z999AA10123456784", "tracking_url": "https://www.ups.com/WebTracking?trackNums=1Z999AA10123456784", "carrier": "UPS", "line_items": [{ "id": "gid://shopify/LineItem/14584740544661", "quantity": 1 }] } ] }, "adjustments": [ { "id": "gid://shopify/SalesAgreement/1234567890", "type": "return", "occurred_at": "2026-03-27T11:00:00-04:00", "status": "completed", "totals": [{ "type": "total", "amount": -3999 }], "description": null, "line_items": [{ "id": "gid://shopify/LineItem/14584740544661", "quantity": -1 }] } ] }, "content": [ { "type": "text", "text": "{\"ucp\":{...},\"id\":\"gid://shopify/Order/6252199051413\",...}" } ] } } ``` ## Order data model An order is the full state of a buyer's purchase after checkout completes. Both `get_order` and [order webhooks](https://shopify.dev/docs/agents/orders/order-webhooks) return the same shape: a top-level envelope with four nested structures (`totals`, `line_items`, `fulfillment`, and `adjustments`). The payload reflects current state at the time of the request or delivery. Shopify doesn't expose historical snapshots. To track changes over time, store webhook deliveries. Monetary values are signed integers in the order's presentment currency, expressed in minor units. `$10.00 USD` is `1000`. Negative values are reductions (refunds, discounts). Positive values are additions. ## Example order ```json { "ucp": { "version": "2026-04-08", "capabilities": { "dev.ucp.shopping.order": [{ "version": "2026-04-08" }] } }, "id": "gid://shopify/Order/6252199051413", "label": "#1042", "checkout_id": "gid://shopify/Checkout/7904f172582fa0de7a67f1839f8ed7ae?key=3d5fe2f0d03a5ecf432a17c6c4c1dde5", "permalink_url": "https://cool-store.myshopify.com/96686207971/orders/eaadd7a71f2656ef2684275972df05da/authenticate", "currency": "USD", "totals": [ { "type": "subtotal", "display_text": "subtotal", "amount": 6298 }, { "type": "fulfillment", "display_text": "fulfillment", "amount": 899 }, { "type": "tax", "display_text": "tax", "amount": 780 }, { "type": "fee", "display_text": "fee", "amount": 0 }, { "type": "total", "display_text": "total", "amount": 7977 } ], "line_items": [ { "id": "gid://shopify/LineItem/14584740544661", "item": { "id": "gid://shopify/ProductVariant/46040179376277", "title": "Large / Black", "price": 3999, "image_url": "https://cdn.shopify.com/s/files/1/2637/1970/products/classic-tee.jpg" }, "quantity": { "original": 2, "total": 1, "fulfilled": 1 }, "totals": [ { "type": "subtotal", "display_text": "subtotal", "amount": 3999 }, { "type": "tax", "display_text": "tax", "amount": 520 }, { "type": "total", "display_text": "total", "amount": 4519 } ], "status": "fulfilled", "parent_id": null } ], "fulfillment": { "expectations": [ { "id": "gid://shopify/Expectation/6252199051413", "line_items": [ { "id": "gid://shopify/LineItem/14584740544661", "quantity": 1 } ], "method_type": "shipping", "destination": { "first_name": "Jane", "last_name": "Smith", "street_address": "123 Main Street", "address_locality": "Brooklyn", "address_region": "NY", "address_country": "US", "postal_code": "11201" } } ], "events": [ { "id": "gid://shopify/Fulfillment/5650582175893?status=created", "occurred_at": "2026-03-25T14:30:00-04:00", "type": "shipped", "tracking_number": "1Z999AA10123456784", "tracking_url": "https://www.ups.com/WebTracking?trackNums=1Z999AA10123456784", "carrier": "UPS", "line_items": [ { "id": "gid://shopify/LineItem/14584740544661", "quantity": 1 } ] }, { "id": "gid://shopify/FulfillmentEvent/8901234567", "occurred_at": "2026-03-26T09:15:00-04:00", "type": "delivered", "tracking_number": "1Z999AA10123456784", "carrier": "UPS", "line_items": [ { "id": "gid://shopify/LineItem/14584740544661", "quantity": 1 } ] } ] }, "adjustments": [ { "id": "gid://shopify/SalesAgreement/1234567890", "type": "return", "occurred_at": "2026-03-27T11:00:00-04:00", "status": "completed", "totals": [{ "type": "total", "amount": -3999 }], "description": null, "line_items": [ { "id": "gid://shopify/LineItem/14584740544661", "quantity": -1 } ] } ] } ``` ### Top-level fields Every order payload starts with the same envelope: identifiers (`id`, `label`, `checkout_id`), the public `permalink_url`, the presentment `currency`, and four nested structures (`totals`, `line_items`, `fulfillment`, `adjustments`) that carry the rest of the state. | Field | Type | Description | | - | - | - | | `ucp` | object | The UCP envelope carries the negotiated `version` and `capabilities` map for this response. | | `id` | string | The order's Shopify Global ID is formatted as `gid://shopify/Order/{numeric_id}`. Use it as your storage key, not as a buyer-facing label. | | `label` | string | The buyer-facing identifier defaults to the order name (for example, `#1042`). Shops with `use_confirmation_number_on_buyer_facing_communication` enabled send a confirmation number instead. | | `checkout_id` | string | The Shopify Global ID of the originating checkout. The value includes a `?key=…` parameter when a confirmation receipt URL has been issued. | | `permalink_url` | string | The merchant's unauthenticated public order status page. It renders a limited buyer-facing view without PII and is the authoritative reference for refund initiation, returns, and the detailed timeline. | | `currency` | string | The presentment currency, expressed as an ISO 4217 code. The currency matches the originating checkout. | | `totals` | array | The order's money breakdown as an array of typed rows. See [Totals](#totals). | | `line_items` | array | The items the buyer purchased, including any later removed by an order edit. See [Line items](#line-items). | | `fulfillment` | object | The buyer-facing expectations and the event log for the order. See [Fulfillment](#fulfillment). | | `adjustments` | array | The committed post-purchase changes to the order. See [Adjustments](#adjustments). | ### Totals `totals` is an array of typed money rows that mirrors the merchant's checkout breakdown. The same structure also appears on each line item under `line_items[].totals`. Each row carries a `display_text` field that currently mirrors the raw `type`. Don't render it directly to buyers. Map types to your own localized labels. | `type` | Sign | When present | | - | - | - | | `items_discount` | ≤ 0 | Shopify includes this row when one or more lines have a line-level discount. It appears at both the order and line level. | | `subtotal` | ≥ 0 | This row is always present at both the order and line level. Its value is the sum of pre-tax line totals after item discounts. | | `discount` | ≤ 0 | Shopify includes this row at the order level when an order-level or cart-level discount is applied (for example, a promo code or automatic discount). | | `fulfillment` | ≥ 0 | This row captures shipping or fulfillment fees. It's always present at the order level. | | `tax` | ≥ 0 | This row is present at both the order and line level for tax-exclusive orders. It's omitted everywhere for tax-inclusive orders, so treat its absence as a signal that prices already include tax. | | `fee` | ≥ 0 | This row captures additional fees, such as marketplace or platform fees. It's always present at the order level. | | `total` | signed | This row is the grand total at the order level and the all-in total at the line level. It's always present. | ### Line items Each line item represents one product variant in the order. Compare `quantity.original`, `quantity.total`, and `quantity.fulfilled` to render edit history, current state, and shipping progress. Removed items stay in the array with `quantity.total: 0` and `status: "removed"` so timeline UIs can show the full edit history. | Field | Type | Description | | - | - | - | | `id` | string | The line item's Shopify Global ID. The ID is stable across the order's lifetime, so use it as the correlation key across deliveries. | | `item.id` | string | The product variant's Shopify Global ID. | | `item.title` | string | The variant's display title, for example `Large / Black`. | | `item.price` | integer | The unit price of the variant at purchase, expressed in minor units of the order's `currency`. | | `item.image_url` | string or null | The URL of the variant's product image on Shopify's CDN. The value is `null` when no image is available. | | `quantity.original` | integer | The quantity the buyer ordered at checkout. This value never changes after the order is placed. | | `quantity.total` | integer | The current active quantity for this line, after any post-purchase order edits. | | `quantity.fulfilled` | integer | The quantity of this line that has shipped or otherwise been fulfilled so far. | | `totals` | array | The per-line money breakdown, using the same row taxonomy as [Totals](#totals). | | `status` | string | A derived rollup of the line's fulfillment progress. Treat the value as an open string. - `processing`: Nothing has been fulfilled yet. - `partial`: Some, but not all, of `quantity.total` has been fulfilled. - `fulfilled`: All of `quantity.total` has been fulfilled. - `removed`: The line was removed through an order edit. | | `parent_id` | string or null | The line item GID of this line's parent, when the line is part of a nested structure such as a bundle or component. The value is `null` for top-level lines. | ### Fulfillment `fulfillment` has two parts: * **`expectations`**: Describes where items go and what's in each package. Render it as the destination card on an order page. * **`events`**: A timeline of fulfillment milestones. Replay it for "Where's my order?" UIs. | Field | Type | Description | | - | - | - | | `expectations` | array | A list of buyer-facing groupings, one per package. Shopify currently emits a single synthetic expectation when the order has a shipping address. Orders without a shipping address (digital-only, in-store pickup) emit an empty array. The canonical UCP specification permits multiple expectations with splits, merges, per-item dates, and `fulfillable_on`. Shopify doesn't emit those today. | | `events` | array | A log of fulfillment milestones, sorted by `occurred_at`. Treat each event's `type` as an open string. The spec allows new values without a breaking change, and internal merchant-only statuses (label printing, fulfillment failures) aren't exposed. The values Shopify currently emits, grouped by category: **Creation*** `shipped`: A fulfillment that requires shipping was created. * `ready`: A pickup or local-delivery fulfillment was created and is ready for the next step.**Tracking*** `carrier_picked_up`: The carrier collected the package from the merchant. * `confirmed`: The carrier confirmed the shipment in their network. * `in_transit`: The package is moving through the carrier network. * `out_for_delivery`: The package is on the truck for final delivery. * `attempted_delivery`: The carrier attempted delivery but couldn't complete it. * `delivered`: The package was delivered to the buyer. * `ready_for_pickup`: The order is ready for the buyer at the pickup location. * `picked_up`: The buyer collected the order at the pickup location. * `delayed`: The shipment is delayed in the carrier network. * `buyer_action_required`: The buyer needs to take an action before delivery can continue (for example, confirm a delivery window or schedule a redelivery). * `returning_to_sender`: The package is being returned to the merchant because it couldn't be delivered.**Cancellation*** `fulfillment_canceled`: A fulfillment was cancelled. | ### Adjustments Adjustments capture committed post-purchase changes (refunds, returns, exchanges, edits, cancellations). Only finalized actions appear. Intermediate states, like a requested-but-unprocessed return, stay out of the array until they complete. The original sale or capture from the checkout is filtered out, so you'll see only post-purchase money movements. For precise timing, the `permalink_url` is the authoritative source. | Field | Type | Description | | - | - | - | | `id` | string | The Shopify Global ID of the underlying sales agreement or transaction. The ID is stable across deliveries, so use it as the deduplication key. | | `type` | string | The kind of adjustment, expressed as an open string. The values Shopify currently emits: - `refund`: A refund was issued to the buyer. Refunds issued through asynchronous payment processors don't appear until they complete, which can take up to 24 hours. - `cancellation`: The order was cancelled. - `return`: Items were returned by the buyer. For most merchants, a return must be processed (refund issued, items received, and so on) before it appears here. A small set of merchants on legacy code paths see returns appear immediately on creation. - `exchange`: Items were exchanged for different items. - `order_edit`: Items were added, removed, or had their quantities changed. - `sale_transaction`, `capture_transaction`, `refund_transaction`: A post-purchase money movement that isn't tied to a sales agreement (for example, a post-purchase upsell or a manual payment capture). | | `occurred_at` | string | The [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) timestamp at which the adjustment was committed. | | `status` | string | The adjustment's lifecycle state. The value is always `"completed"` today. The schema permits `"pending"` and `"failed"`, but Shopify only emits adjustments after they finalize. | | `totals` | array | A single-row array with one `total` entry carrying a signed `amount`. A negative `amount` means money is flowing back to the buyer. A positive `amount` means net money is owed (for example, an exchange to a more expensive item). | | `description` | string or null | Reserved for a future human-readable description. The value is always `null` in v1. | | `line_items` | array | The line items affected by the adjustment, each with a signed `quantity`. The array may be empty for adjustments not tied to specific lines (for example, standalone payment-only transactions). | ## Error handling UCP distinguishes between protocol errors and business outcomes. * **Business outcomes:** Application-level results from successful request processing, returned as JSON-RPC `result` with the UCP envelope and a `messages` array. * **Protocol errors:** Transport-level failures (authentication, rate limiting, unavailability) that prevent request processing. Returned as JSON-RPC `error` with code `-32000`, or `-32001` for discovery errors. See the [Cart MCP binding](https://ucp.dev/2026-04-08/specification/cart-mcp/#error-handling), the [Checkout MCP binding](https://ucp.dev/2026-04-08/specification/checkout-mcp/#error-handling), and [Core Specification error handling](https://ucp.dev/2026-04-08/specification/overview/#error-handling) for the complete error code registry and examples. ### Protocol errors Transport-level failures that prevent the request from being processed (for example authentication failure, rate limiting, or service unavailability) are returned as JSON-RPC `error` with code `-32000`, or `-32001` for discovery errors. These are not application outcomes: the server did not complete the operation. When the server rate-limits your request, retry after the delay specified by the HTTP `Retry-After` response header. Clients should honor `Retry-After` and apply exponential backoff with jitter when the header is absent. Don't retry inside the same checkout or payment lifecycle without an [`idempotency-key`](https://ucp.dev/2026-04-08/specification/overview/#idempotency). ## Protocol error (JSON-RPC error) ```json { "jsonrpc": "2.0", "id": 1, "error": { "code": -32000, "message": "Unauthorized" } } ``` ### Order error codes When `get_order` can't return an order, the response sets `result.isError` to `true` and includes a `messages` array on `result.structuredContent`. Always check `messages` before reading order fields. The codes below diverge in name from the canonical UCP specification's `not_found` and `unauthorized`, but carry equivalent semantics. | Code | Severity | Cause | | - | - | - | | `invalid_order_id` | `recoverable` | The `id` argument isn't a valid Shopify Order GID. Reformat and retry. | | `order_not_found` | `unrecoverable` | The order doesn't exist or wasn't placed through your agent. Don't retry. | | `orders_not_allowed` | `unrecoverable` | The token is missing the `read_global_api_orders` scope. Mint a new token with the correct scope. | A request for an order that doesn't exist or wasn't placed through the requesting agent returns `order_not_found`. Immediately after [`complete_checkout`](https://shopify.dev/docs/agents/carts-and-checkout/checkout-mcp#complete_checkout), this can also surface transiently while the order propagates. If you're calling `get_order` right after checkout completes, wait a few seconds and retry. ## Order error (result with messages) ```json { "jsonrpc": "2.0", "id": 1, "result": { "isError": true, "structuredContent": { "messages": [ { "code": "order_not_found", "content": "The requested order does not exist", "severity": "unrecoverable" } ] } } } ```