Skip to main content

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.

Order MCP implements the UCP order capability (dev.ucp.shopping.order, version 2026-04-08). The capability exposes a single tool, get_order, which returns the full current state of an order.

For background, see About orders. For the canonical specification, see the Order capability and the Order MCP binding.

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 instead.

Anchor to Use with the AI ToolkitUse with the AI Toolkit

The Shopify AI Toolkit installs the ucp skill, which lets agents in Cursor, Claude Code, and other compatible IDEs call Order MCP by name. Ask your assistant in natural language ("track order <id> from <shop>") and the skill picks the right UCP CLI command, or run ucp order get directly in a terminal. Pair either mode with the order webhooks delivered to your endpoint to keep your local view in sync.

Use --view and --format md to project the response for the buyer; use --format json to feed it back into your agent loop.

ucp order get gid://shopify/Order/789 \
--business https://{shop}.example.com
ucp order get gid://shopify/Order/789 \
--business https://{shop}.example.com \
--view 'result.{id: id, status: financial_status, fulfillment: fulfillment_status, total: totals[-1].amount}' \
--format md

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.

Use caseRecommended surface
Proactive notifications and timeline UIsOrder webhooks
Buyer-initiated "Where's my order?" viewsget_order
Reconciling missed webhook deliveriesget_order
Scheduled pollingNot recommended. Process incoming order webhook deliveries instead.

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 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.

POST

https://api.shopify.com/auth/access_token

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

{
"access_token": "f8563253df0bf277ec9ac6f649fc3f17",
"scope": "read_global_api_orders",
"expires_in": 3599
}

Order MCP shares the merchant's UCP MCP endpoint with Cart MCP and 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) for capability negotiation.

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

{
"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"
}
}
}

Caution

Tool use is subject to access and rate limiting that scale with how your agent identifies itself. See 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: Fetch the current state of an order placed through your agent.

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 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.

meta•objectRequired

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

The Shopify Global ID of the order. Format: gid://shopify/Order/{id}.

Anchor to Call from the Shopify AI Toolkit and UCP CLICall from the Shopify AI Toolkit and UCP CLI

With the Shopify AI Toolkit installed, ask your assistant in natural language ("where's my order from <shop>?") and the ucp skill picks the right UCP CLI command. You can also run the command directly in a terminal:

Fetch order state

ucp order get gid://shopify/Order/789 \
--business https://{shop}.example.com

Anchor to Call the tool directlyCall the tool directly

POST

https://{shop-domain}/api/ucp/mcp

{
"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

{
"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\",...}" }
]
}
}

An order is the full state of a buyer's purchase after checkout completes. Both get_order and 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

{
"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 }
]
}
]
}

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.

FieldTypeDescription
ucpobjectThe UCP envelope carries the negotiated version and capabilities map for this response.
idstringThe 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.
labelstringThe 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_idstringThe Shopify Global ID of the originating checkout. The value includes a ?key=… parameter when a confirmation receipt URL has been issued.
permalink_urlstringThe 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.
currencystringThe presentment currency, expressed as an ISO 4217 code. The currency matches the originating checkout.
totalsarrayThe order's money breakdown as an array of typed rows. See Totals.
line_itemsarrayThe items the buyer purchased, including any later removed by an order edit. See Line items.
fulfillmentobjectThe buyer-facing expectations and the event log for the order. See Fulfillment.
adjustmentsarrayThe committed post-purchase changes to the order. See Adjustments.

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.

typeSignWhen present
items_discount≤ 0Shopify includes this row when one or more lines have a line-level discount. It appears at both the order and line level.
subtotal≥ 0This 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≤ 0Shopify 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≥ 0This row captures shipping or fulfillment fees. It's always present at the order level.
tax≥ 0This 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≥ 0This row captures additional fees, such as marketplace or platform fees. It's always present at the order level.
totalsignedThis row is the grand total at the order level and the all-in total at the line level. It's always present.

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.

FieldTypeDescription
idstringThe 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.idstringThe product variant's Shopify Global ID.
item.titlestringThe variant's display title, for example Large / Black.
item.priceintegerThe unit price of the variant at purchase, expressed in minor units of the order's currency.
item.image_urlstring or nullThe URL of the variant's product image on Shopify's CDN. The value is null when no image is available.
quantity.originalintegerThe quantity the buyer ordered at checkout. This value never changes after the order is placed.
quantity.totalintegerThe current active quantity for this line, after any post-purchase order edits.
quantity.fulfilledintegerThe quantity of this line that has shipped or otherwise been fulfilled so far.
totalsarrayThe per-line money breakdown, using the same row taxonomy as Totals.
statusstringA 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_idstring or nullThe 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 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.
FieldTypeDescription
expectationsarrayA 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.
eventsarrayA 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 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.

FieldTypeDescription
idstringThe Shopify Global ID of the underlying sales agreement or transaction. The ID is stable across deliveries, so use it as the deduplication key.
typestringThe 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_atstringThe RFC 3339 timestamp at which the adjustment was committed.
statusstringThe adjustment's lifecycle state. The value is always "completed" today. The schema permits "pending" and "failed", but Shopify only emits adjustments after they finalize.
totalsarrayA 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).
descriptionstring or nullReserved for a future human-readable description. The value is always null in v1.
line_itemsarrayThe 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).

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, the Checkout MCP binding, and Core Specification error handling for the complete error code registry and examples.

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.

Protocol error (JSON-RPC error)

{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "Unauthorized"
}
}

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.

CodeSeverityCause
invalid_order_idrecoverableThe id argument isn't a valid Shopify Order GID. Reformat and retry.
order_not_foundunrecoverableThe order doesn't exist or wasn't placed through your agent. Don't retry.
orders_not_allowedunrecoverableThe 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, 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)

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"isError": true,
"structuredContent": {
"messages": [
{
"code": "order_not_found",
"content": "The requested order does not exist",
"severity": "unrecoverable"
}
]
}
}
}