Shopify Agentic Commerce Audit: A Catalog Readiness Checklist for AI Agents
Shopify's agentic discovery surface is documented, inspectable and testable, so a catalog audit does not have to be an opinion. This checklist runs seven checks in the order failures cascade: whether an agent can reach the catalog at all, whether every record carries a stable identity, whether the attribute mapping survives a filter, whether you can tell what the merchant wrote from what Shopify generated, whether the page carries the markup the page-reading surfaces require, whether the availability signals say what a buyer will assume, and whether the integration respects the documented operational rules. Every measurement below was taken on 2026-09-18 against live stores.
One correction before the first check. The identity layer Shopify's own catalog documentation commits to is its GIDs and UPIDs, plus handle and url. A case-insensitive scan of the seven Shopify catalog documentation pages used for this audit returns 0 occurrences of gtin, 0 of mpn and 0 of barcode, and the live catalog responses captured for this page contain the same three zeros. The UCP schema does define an optional variant barcodes array, and ACP requires only id and title on a variant. So "no GTINs in the catalog API" is not a finding against a merchant. GTIN and MPN matter on Google's merchant-listing structured data, which is a different pipe with a different contract (Step 5).
Three pipes, three jobs
The most common audit error is treating "AI agents" as one surface. Three separate contracts carry product data, with three schemas and three failure modes.
| Pipe | Transport | Read by | Carries | Typical failure |
|---|---|---|---|---|
JSON-LD Product/Offer on the product page | HTML | Google Search merchant listings and any crawler that renders the page | name, image, a nested offers, gtin/mpn where they exist, availability | markup present but stale; no Offer; price of zero |
| UCP catalog over MCP | POST JSON-RPC to /api/ucp/mcp | Shopify's agent surfaces and any third-party agent that speaks UCP | GID/UPID identity, title, description, price_range, media, variants[], taxonomy categories | thin records; wrong taxonomy leaf; unavailable items silently absent |
| ACP product feed | merchant-pushed REST | agentic checkout flows | product and variant ids, price, availability, optional barcodes, seller | stale feed; assuming the feed reserves stock |
ACP states the rationale for the middle pipe plainly: “Agents use the merchant-provided content instead of scraping storefront HTML.”
Blur them and the findings stop meaning anything: a store can ship flawless product markup and still be missing from the catalog API, or be perfectly indexed by Google and invisible to an agent shopping inside a chat assistant. Every check below names the pipe it tests.
How to run this audit
- One pipe per check, named in the finding. "Not in the catalog API" and "not in Google's merchant listings" are different defects with different owners.
- Keep the raw response, dated. Two of the four storefronts probed here answered
/.well-known/ucpwith 404 on 2026-09-18, and endpoint URLs are documented as subject to change. - Three verdicts, not two. PASS, FAIL and NOT TESTABLE. Nothing in the fetched documentation exposes per-agent ranking, so "how do we rank inside ChatGPT" is NOT TESTABLE rather than failed.
- The person who ran the request signs the finding. A finding built from a vendor's screenshot is a quote, not a test.
Step 1 — Reachability: can an agent open the catalog?
“Shopify provides two catalog interfaces for AI agents to discover and retrieve products. Global Catalog searches across all Shopify merchants, while Storefront Catalog is scoped to a single merchant's store. Both implement the UCP Catalog capability , but they differ in scope, authentication, and available features.”
The endpoints are POST https://catalog.shopify.com/api/ucp/mcp for the cross-merchant surface and POST https://STORE_DOMAIN/api/ucp/mcp for a single store. Both are keyless but profile-gated: “The /api/ucp/mcp endpoint requires an agent profile . Every request must include a meta.ucp-agent.profile URL pointing to your agent's UCP profile. The returned tools depend on the capabilities your agent advertises.”
The merchant-side half of that handshake is a published business profile: “Business profile: Published by the merchant or platform operating the commerce API, typically at /.well-known/ucp on the business origin.” “On Shopify, this exists at the merchant storefront ( {shop}.myshopify.com/.well-known/ucp ).” Google uses the same manifest shape for its own commerce surfaces.
The test
GET https://STORE_DOMAIN/.well-known/ucp— expect HTTP 200 and a JSON manifest whosecapabilitiesnamedev.ucp.shopping.catalog.searchanddev.ucp.shopping.catalog.lookup. Measured 2026-09-18: allbirds.com and deathwishcoffee.com served 4.6 KB manifests with both, plus thedev.shopify.catalogextension; gymshark.com and shopify.com returned 404.tools/listagainst the store's/api/ucp/mcpwithmeta.ucp-agent.profileset — expectsearch_catalog,lookup_catalogandget_productin the list. Measured: the storefront returned 13 tools, the three catalog tools among them.tools/call search_catalogwith{"catalog":{"query":"running shoes"}}— expect products rather than an error.
Pass means all three, and the first one runs first because a 404 there reproduces itself in every later check. A request missing the profile fails loudly, which is evidence in itself: the two stores answered a profile-less catalog call with HTTP 422, code -32001 “UCP discovery failed” and data.code “invalid_profile_url”, while a profile-less tools/list still answered 200.
Step 2 — Identity: a stable key on every record
Search results arrive keyed and clustered on Shopify's own identifiers: “Find products by keyword. Use this when a buyer describes what they want in natural language. Global Catalog returns products from across all Shopify merchants, clustered by Universal Product ID (UPID); Storefront Catalog returns products scoped to a single store.” The routing-facing fields are documented separately, which matters to anyone building links or an id map: “URL-safe slug for SEO-friendly URLs (e.g., 'blue-runner-pro'). Use id for stable API references.” “Canonical product page URL.” The purchasable unit is the variant, whose GID is what a checkout receives: “Global ID (GID) uniquely identifying this variant. Used as item.id in checkout.”
Batch behaviour is documented and was confirmed to be enforced: “Array of product or variant identifiers (1 to 50). Accepts gid://shopify/p/{upid} , gid://shopify/ProductVariant/{id} , and http or https Shopify product URLs.” The per-store ceiling is lower than the cross-merchant one — the storefront lookup schema caps a request at 10 ids, the global schema at 50 — and the protocol sets a lower floor than either: “Implementations MUST support lookup by product ID and variant ID. Implementations MAY additionally support secondary identifiers such as SKU or handle, provided these are also fields on the returned product object.”
The test
- Take ten product ids out of a
search_catalogresponse and re-request them throughlookup_catalog. Assert that the resolved count equals the requested count, or that every unresolved id produced a message. Measured 2026-09-18: two valid product GIDs from a store's own search response resolved to one product, with no message for the other, while an obviously invalid id produced an explicitnot_foundmessage. A 200 status is not evidence that a batch resolved. - Probe the ceilings in the request, not just the documentation. Measured: an 11-identifier storefront request and a 52-identifier global request were both rejected, with error text naming the limit.
- Probe identifier types rather than assuming them. The protocol makes product and variant ids mandatory and SKU, handle and barcode lookup optional, so a design that depends on SKU lookup is unverified until tested. Measured: ten variant GIDs taken from the storefront's own search resolved to zero products on that surface.
- Do not mix namespaces. Measured: two storefront product GIDs sent to the cross-merchant endpoint came back as
not_foundmessages naming each id, because the global surface keys on its own UPID form.
Step 3 — Attribute mapping: does the filter find the product?
Discovery filters are taxonomy-driven, not keyword-driven. “Filter by product category. Each item is a taxonomy category GID (for example, "gid://shopify/TaxonomyCategory/123" ). Multiple values use OR logic.” Attributes are a short, named list rather than free text — Shopify's extension supports Color, Size and Target gender, and unsupported attribute names are ignored and returned in messages. Filters combine additively: “Filter criteria to narrow search results. All specified filters combine with AND logic.”
A Shopify-specific extension (dev.shopify.catalog) is what turns a base UCP response into a merchant-usable record: when it is active, products carry merchant-curated collections and gift-card flags, and variants carry checkout prerequisites, selling plans and a direct checkout URL.
The test
- Pick three products from three different taxonomy leaves and query each with its leaf category GID plus a keyword. A product that disappears is a mapping defect, not a ranking result.
- Run one query per supported attribute and reconcile hit counts against a manual collection count in the admin. A filter that was silently ignored looks identical to an attribute that matched nothing.
- Read the
messagesarray of every response — the only place the difference between "filtered" and "ignored" is visible. - Re-run one query with
filters.available=falseand diff the two sets. The documented default returns only sale-ready items (“Filter by availability. Defaults to true (only sale-ready items). Set to false to include unavailable items.”), yet the live probe for this page returned 10 products and 100 variants of which 88 carriedavailability.available=false, so verify the default per store rather than assuming it.
Step 4 — Inferred fields: what did Shopify write?
“Some Global Catalog response fields are generated or enriched by Shopify, and are marked Inferred in the reference. These fields might not always be present or might vary in accuracy depending on available product data. Treat them as discovery and merchandising signals, not as merchant-authored source text.” “Some fields might be inferred by Shopify's AI and might not always be present or have varying accuracy depending on available product data. Inferred fields are marked throughout the Catalog MCP and API reference docs with the Inferred label.”
On the cross-merchant surface the inferred set is enumerated:
| Field | What Shopify generates |
|---|---|
| description | Product description generated or enriched by Shopify. |
| options | Product options normalized for catalog discovery and variant selection. |
| metadata.attributes | Product attributes such as material, style, and occasion. |
| metadata.tech_specs | Technical specifications. |
| metadata.top_features | Top product features. |
| metadata.unique_selling_points | Unique selling propositions. |
| variants[].condition | Product condition labels, such as "new" or "secondhand" . |
The test
- Pull the same product from
search_catalog, from the per-store surface and from the product page. Three descriptions can be three different texts; only one of them is the merchant's copy. - Grade the inferred set as signals, not as data quality. An absent inferred attribute is not a defect a merchant can be marked down for.
- Where the merchant's own wording carries a claim, a price statement or a disclaimer, verify it on the page and in the feed. The inferred description is generated, so it is not the place to check compliance.
Step 5 — Schema validation: the page-side contract
Page markup is a real pipe, but a narrower one than it is usually described as. Measured: the string json-ld appears 0 times across Shopify's seven catalog documentation pages and the UCP and ACP specifications — 22 files scanned. So "AI agents read your JSON-LD" is only true for a named surface: Google Search's merchant listings, plus any crawler that renders the page. Google's requirement statement is blunt: “You must include the required properties for your content to be eligible for display as a rich result. You can also include the recommended properties to add more information to your structured data, which could provide a better user experience.”
On Google the required properties are name, image and a nested offers, and for merchant listings the merchant has to be the seller. GTIN and MPN sit in the recommended list, not the required one, which is where the circulating checklists invert the story: “While you can use the generic gtin property for all GTINs, we recommend that you use the most specific GTIN that applies to your product, as this is the most accurate representation of the product.” The price rule is also stricter here than for plain product snippets: “Unlike product snippets, merchant listing experiences require a price greater than zero.”
The test
- Run three product pages through the Rich Results Test: a top seller, a variant-heavy product, and a product with no barcode at all.
- Confirm one
Productblock per page with a nestedOfferand a price above zero, and check variant-level identifiers and prices on the variant-heavy product. A parent-only markup block leaves the variants an agent must choose between without identity. - Record which identifiers exist rather than scoring them pass or fail. A catalog with zero barcode coverage can still be fully discoverable through the catalog API; "no cross-retailer identity" is a Google-surface gap with a Google-surface fix.
One more uncoupled claim, since it leads most third-party scorecards: the string /products.json appears 0 times across the same seven Shopify catalog pages. It is a long-standing theme endpoint, useful to scrapers, but nothing in the documentation of the agentic catalog makes it a floor signal.
Step 6 — Availability: a signal, not a reservation
The single most over-promised item in agentic commerce is stock. ACP is explicit: “Availability in a feed does not reserve inventory. Reservation semantics, if any, occur during checkout.” and “Agents MUST treat checkout responses as authoritative even when they differ from feed data.” What a merchant can honestly offer is a quality signal, with a fulfillment state rather than a boolean: “Qualifies available with fulfillment state. Well-known values: `in_stock`, `backorder`, `preorder`, `out_of_stock`, `discontinued`.” ACP's variant state list is wider still: “Extensible fulfillment state for the variant. Known values include in_stock, limited_stock, backorder, preorder, out_of_stock, and discontinued.”
The documented shape of that object, from the catalog reference, is:
{"availability": {"available": true, "status": "in_stock", "running_low": false}}
In the live per-store responses captured for this page the object carried only available — the status and running_low keys documented for the cross-merchant surface did not appear on any of the 100 variants returned. Test for the ladder; do not assume the boolean is the whole contract.
Feeds behave differently from a search call, and the difference decides how a merchant withdraws a product: “is an upsert operation. Products omitted from the request remain unchanged.” A feed's own timestamp is the freshness evidence an agent has: “RFC 3339 timestamp for the most recent update applied to the feed.”
The test
- Call
get_productand confirm every variant carries an availability object with a fulfillment state, not a bare boolean. - Check that the store's states cover the ladder it needs. Preorder, backorder and discontinued cannot be expressed by a flag alone.
- Test the withdrawal path. Omit a product from a feed update, read the feed back and record what happened: the documented behaviour is an upsert, so removal needs a full replacement or an explicit unavailable/discontinued state rather than silence.
- Capture the feed's last-updated timestamp. If nobody can produce it, the feed is untestable.
Step 7 — Operational rules that fail an integration
Three documented rules fail the integration rather than the catalog, and they are the fastest way to catch an agency-built dashboard that quietly breaks the terms it depends on. Images may not be cached or re-used — they must be rendered in real time, in connection with the merchant's own listing — and catalog results may not be cached at all, because they carry merchant preferences on pricing, availability and presentation. Catalog queries are also rate-limited, and keyless access cannot buy a higher limit: “Catalog queries are subject to rate limits. Keyless catalog access doesn't support rate limit increases. To request a rate limit increase, go to the Shopify Help Center , select Chat with us , log in, and choose your Partner organization to contact us.”
The test
- Ask whoever built any catalog dashboard what it stores. A nightly snapshot of catalog results is a documented violation, not a caching optimisation, and rehosted images are the same class of defect.
- Since keyless access cannot get a rate-limit increase, check that the integration batches lookups to the documented ceilings instead of re-running a search for one product.
- Record the agent profile URL the integration sends. The tool set returned depends on the capabilities that profile advertises, so two agents hitting the same store can legitimately receive different tools.
The audit checklist
Eighteen checks, the surface each one tests, and the evidence that closes it.
| # | Check | How to test it | Pass condition |
|---|---|---|---|
| 1 | Reachability | GET https://STORE_DOMAIN/.well-known/ucp | 200, manifest names catalog.search and catalog.lookup |
| 2 | MCP endpoint | tools/list on /api/ucp/mcp with an agent profile | the three catalog tools are present |
| 3 | Profile gating | Repeat that call with no profile | documented error path, not a silent success |
| 4 | Search | search_catalog with a natural-language query | products returned, each with an id |
| 5 | Lookup fidelity | Re-request the returned ids via lookup_catalog | resolved count equals requested count, or a message per miss |
| 6 | Identifier namespace | Send storefront GIDs to the global endpoint, and the reverse | not_found messages confirm ids are per-surface |
| 7 | Routing fields | Fetch each product's handle and url | both resolve 200 on the storefront |
| 8 | Taxonomy mapping | One query per product using its leaf category GID | every product is found, or the mapping is the defect |
| 9 | Attribute filters | One query per supported attribute (Color, Size, Target gender) | hit counts reconcile with a manual collection |
| 10 | Filter diagnostics | Read the messages array of every response | no entries reporting ignored filter values |
| 11 | Availability default | Re-run one query with filters.available=false | the difference matches a manual sale-ready check |
| 12 | Inferred fields | Diff search_catalog text against the page copy | the merchant's own wording is identified, not assumed |
| 13 | Page markup | Rich Results Test on a top seller, a variant-heavy product, a barcode-free product | one Product block, nested Offer, price above zero |
| 14 | Cross-retailer identity | Barcode and mpn coverage in the page markup | reported as a Google-recommended gap, never an API failure |
| 15 | Availability shape | get_product on a variant | a fulfillment state, not a bare boolean |
| 16 | Feed semantics | Omit a product from an update, then read the feed back | documented upsert behaviour confirmed and written down |
| 17 | Feed freshness | Read the feed's updated_at | present, RFC 3339 and recent |
| 18 | Operational rules | Ask the integration's owner what their dashboard stores | no cached results, no rehosted images, lookups batched |
What this audit cannot tell you
- Nothing about per-agent ranking. The fetched documentation exposes no ranking or impression data for third-party agents, so a claim about position inside a given assistant is speculation, not a finding.
- Nothing about real-time stock. Reservation is a documented non-goal; checkout is the authoritative source.
- Nothing about the ChatGPT merchant surface.
chatgpt.com/merchantsanswered HTTP 403 to every fetch attempted for this audit on 2026-09-18, so no claim about it appears here. - Nothing permanent. Endpoint paths and field lists are versioned and move; re-run the captures against the vendor's current documentation before a client build.
The most-shared framing of this surface is published commentary, not a vendor standard, and it is worth citing correctly. The useful line in it is “An AI agent consuming the Catalog API reads records, not pages. Your carefully crafted collection-page copy does not travel through this channel. Only the structured product record does.” Third-party scorecards mix the pipes in the other direction — one circulating rubric makes /products.json and GTIN coverage floor signals, which the documented surfaces above do not support. Use those rubrics for ideas; audit against the primary schema.
The agency-side half of the same work: how to scope, staff and price the build once the audit has found the gaps. That is written for providers at the Shopify catalog readiness playbook.
Frequently asked questions
Is GTIN required for a Shopify catalog to be discoverable by AI agents?
No, not on this surface. A scan of Shopify's seven catalog documentation pages returns zero mentions of gtin, mpn or barcode, and the live catalog responses captured for this audit contain none of the three either. The UCP schema allows an optional variant barcodes array and ACP requires only an id and a title. GTIN and MPN are recommended properties in Google's merchant-listing markup, which is where cross-retailer identity gets fixed.
What endpoints does Shopify expose to AI agents?
Two UCP catalog endpoints over MCP: a cross-merchant one at https://catalog.shopify.com/api/ucp/mcp and a per-store one at https://STORE_DOMAIN/api/ucp/mcp. Both are keyless, but every request carries an agent profile URL, and the storefront origin answers /.well-known/ucp with a business profile manifest declaring which catalog capabilities the shop supports.
Which tools does the catalog expose?
search_catalog for natural-language discovery, lookup_catalog for retrieval by identifier, and get_product for a single product with all option combinations, availability signals and checkout links. A live storefront tools/list call on 2026-09-18 listed 13 tools in total: those three alongside cart, checkout and order tools.
How many identifiers can one lookup resolve?
Ten on the per-store surface and 50 on the cross-merchant one. Both ceilings were tested: an 11-identifier storefront request and a 52-identifier global request were rejected with an error naming the limit. The specification requires product and variant id lookup, while SKU, handle and barcode lookup are optional.
Does the catalog API give agents real-time inventory?
No. Availability travels as a discovery signal, and ACP lists inventory reservation as an explicit non-goal: reservation, if any, happens during checkout, and checkout responses are authoritative even when they disagree with feed data.
Do AI agents read my product page's JSON-LD?
Only on surfaces that read pages, chiefly Google Search's merchant listings. The string json-ld does not appear in Shopify's catalog documentation or in the UCP and ACP specifications. Product markup with a nested Offer and a price above zero matters on Google; it is not the catalog API's contract.
Can a merchant see how products rank inside AI agents?
Not through these APIs. Nothing in the catalog, UCP or ACP documentation exposes per-agent ranking or impression data, so rank questions are untestable from the merchant side. Test reachability, identity and attribute mapping instead.
Sources
Primary: Shopify — Agentic commerce: Catalogs, with the Global Catalog and Storefront Catalog pages and their extensions; Shopify — About profiles; the UCP specification including the catalog search and lookup schemas; the ACP product-feed RFC and its JSON Schema; Google Search Central — Merchant listing (Product, Offer) structured data and the Google Merchant UCP guide.
Live probes, all captured 2026-09-18: GET /.well-known/ucp on allbirds.com, deathwishcoffee.com, gymshark.com and shopify.com; tools/list, search_catalog, lookup_catalog and get_product JSON-RPC calls against https://www.allbirds.com/api/ucp/mcp and https://catalog.shopify.com/api/ucp/mcp; and a case-insensitive count of gtin, mpn, barcode, json-ld and /products.json across the seven saved Shopify catalog pages and the UCP and ACP specifications.
Secondary and comparison only: Taylor Sicard's June 2026 essay on the Shopify Catalog API, re-shared to X on 16 September 2026; a third-party agentic-storefronts rubric at catalogscan.com, cited as a scorecard and not as a standard. Nothing on this page is legal, tax or platform-policy advice; platform APIs change and endpoint behaviour should be re-verified against the vendor's current documentation before a client build.