Home › Blog › Return policy schema
Shopify return policy schema for AI shopping agents: MerchantReturnPolicy, hasMerchantReturnPolicy, and conditional final-sale overrides
73% of Shopify stores have no MerchantReturnPolicy in their product structured data. AI shopping agents rank return window as a top-3 decision factor — without hasMerchantReturnPolicy on your Offer, the agent assumes the worst and ranks your competitor who has it.
Contents
How AI shopping agents use return policy data
When a shopper asks ChatGPT Shopping "which running shoe has the best return policy?" or Perplexity "can I return this jacket if it doesn't fit?", the agents are not reading your Shopify return policy page — they are reading your product's hasMerchantReturnPolicy structured data. The difference matters: a return policy page requires text extraction with ambiguous results; a MerchantReturnPolicy block with returnPolicyCategory: MerchantReturnFiniteReturnWindow and merchantReturnDays: 30 is unambiguous and machine-readable in under 50ms.
In practice, AI shopping agents use return policy structured data in three ways:
- Comparison filtering. ChatGPT Shopping and Google AI Mode display return windows alongside price and shipping when presenting product comparison tables. Products without
hasMerchantReturnPolicyshow "return policy unknown" — a conversion-killing label next to a competitor who has "free 30-day returns" in clean structured data. - Exclusion from curated lists. Agents compiling "top gifts under $50 with easy returns" or similar intent-driven lists programmatically filter for
returnPolicyCategory: MerchantReturnFiniteReturnWindowwithmerchantReturnDays ≥ 30. If your policy is missing, you are excluded from the list regardless of your actual policy. - Trust signal aggregation. Return policy is one of several trust signals — alongside
AggregateRating, price guarantee, and structured trust signals — that agents combine into a confidence score for whether to recommend a product. A free, no-hassle return policy expressed in structured data raises that score meaningfully.
MerchantReturnPolicy, implementing it correctly is a low-competition signal that measurably improves your visibility in AI shopping results right now — before it becomes table stakes.
MerchantReturnPolicy: schema anatomy and all properties
The MerchantReturnPolicy type is defined in schema.org/MerchantReturnPolicy. Here is every property you need to know, with Shopify-specific guidance for each. See also: MerchantReturnPolicy schema reference for ecommerce.
Required
MerchantReturnEnumeration vocabulary. Values: MerchantReturnFiniteReturnWindow (has a return window — use with merchantReturnDays), MerchantReturnNotPermitted (final sale, no returns), MerchantReturnUnlimitedWindow (no time limit), MerchantReturnUnspecified (policy not stated — avoid this; it is the default assumed state when the property is absent). Always prefix with https://schema.org/.Required if finite window
returnPolicyCategory is MerchantReturnFiniteReturnWindow. Common values: 30, 60, 90. Measured from the date of delivery or purchase depending on your policy — note that schema.org does not distinguish between these, so pick the more conservative value if they differ.Recommended
ReturnActionAccessibilityEnumeration: ReturnByMail, ReturnInStore, ReturnAtKiosk. Accepts a single URL or an array of URLs if multiple methods are offered.Recommended
FreeReturn (seller pays), ReturnShippingFees (buyer pays return shipping), OriginalShippingFees (original shipping is not refunded), NoFees (no fees at all), RestockingFees (restocking fee applied). Use FreeReturn if you cover return shipping — this is the highest-conversion value for AI agents.Recommended
"US", "GB", "CA") or a Country object. For US-only stores, use "US". For multi-region policies, repeat the MerchantReturnPolicy block per country — do not try to list multiple countries in one block.Recommended
https://yourstore.com/policies/refund-policy. Shopify auto-generates this URL for all stores.Optional
"Standard 30-Day Policy", "Final Sale — No Returns". Not required for a single-policy store but valuable when you have conditional policies per product.Optional
returnMethod: ReturnInStore already communicates. Include it if you want to satisfy older crawlers that check for this boolean rather than parsing returnMethod.Site-wide vs. per-product: the architecture decision
Most Shopify stores have a single return policy that applies to all products — free 30-day returns, say — with exceptions carved out for certain product types (final sale clearance items, digital products, swimwear). The correct architecture expresses this as:
- A site-wide default policy attached to the
Organizationentity intheme.liquidvia thehasMerchantReturnPolicyproperty. - A per-product policy override on the
Offerobject in the product page JSON-LD, using a Liquid conditional to substitute the appropriateMerchantReturnPolicyblock when the product is an exception case.
This two-layer approach means most products inherit the site-wide policy (no extra work per product), while exception products (final sale, swimwear, digital) get the correct override automatically if they carry the right product tag.
MerchantReturnPolicy only on your Organization entity is not enough. AI shopping agents crawl product pages individually — the agent checking your running shoe does not also fetch your homepage to read your Organization entity. You must also attach hasMerchantReturnPolicy directly to the Offer on each product page. Without the product-level link, 73% of stores that skip it and the stores that put it only on Organization both look identical to the agent: no policy found.
{
"@type": "Offer",
"price": "89.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
/* hasMerchantReturnPolicy absent */
}
{
"@type": "Offer",
"price": "89.00",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"returnPolicyCategory":
"https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod":
"https://schema.org/ReturnByMail",
"returnFees":
"https://schema.org/FreeReturn"
}
}
For the full reference on return policy structured data patterns — including multi-country policy blocks and extended holiday return windows — see our complete MerchantReturnPolicy structured data guide.
Conditional policies: final sale, sale items, and category overrides
The power of Shopify's Liquid templating is that you can express return policy exceptions declaratively using product tags, without creating per-product metafields or custom logic in your theme editor. Three common conditional patterns:
Pattern 1 — Final sale (no returns)
Tag final-sale products with final-sale. In your product JSON-LD snippet, check for the tag and output MerchantReturnNotPermitted:
{%- if product.tags contains 'final-sale' -%}
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"name": "Final Sale — No Returns",
"returnPolicyCategory":
"https://schema.org/MerchantReturnNotPermitted",
"applicableCountry": "US"
}
{%- else -%}
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"name": "Standard 30-Day Return",
"returnPolicyCategory":
"https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn",
"applicableCountry": "US",
"merchantReturnLink":
"https://{{ shop.permanent_domain }}/policies/refund-policy"
}
{%- endif -%}
Pattern 2 — Reduced return window for sale items
Some stores offer a shorter return window (e.g., 14 days) for clearance or sale-priced items while keeping 30 days for full-price products. Use a compare_at_price check — if the product has a compare-at price, it is on sale:
{%- assign return_days = 30 -%}
{%- if variant.compare_at_price > variant.price -%}
{%- assign return_days = 14 -%}
{%- endif -%}
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"returnPolicyCategory":
"https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": {{ return_days }},
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn",
"applicableCountry": "US"
}
Pattern 3 — Product type override via metafield
For stores with strict category-level policies (swimwear, pierced jewelry, opened software), use a product metafield catalogscan.return_days (integer type). When present, override the global default; when absent, use the global default:
{%- assign return_days = product.metafields.catalogscan.return_days
| default: 30 -%}
{%- assign no_return = product.metafields.catalogscan.no_return
| default: false -%}
{%- if no_return == true or no_return == 'true' -%}
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"name": "No Returns",
"returnPolicyCategory":
"https://schema.org/MerchantReturnNotPermitted",
"applicableCountry": "US"
}
{%- else -%}
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"returnPolicyCategory":
"https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": {{ return_days }},
"returnMethod": [
"https://schema.org/ReturnByMail",
"https://schema.org/ReturnInStore"
],
"returnFees": "https://schema.org/FreeReturn",
"applicableCountry": "US"
}
{%- endif -%}
Related guides
Liquid implementation for Shopify Dawn
The following is a complete, production-ready implementation for Shopify Dawn. It handles all three conditional patterns in a single snippet you include once. The approach uses a dedicated snippet file so you can update the policy in one place without editing every theme template.
Step 1 — Create snippets/product-return-policy.liquid
Add this file to your theme's snippets/ folder:
{%- comment -%}
Snippet: product-return-policy.liquid
Outputs the hasMerchantReturnPolicy JSON-LD fragment for a product Offer.
Usage: {% render 'product-return-policy', product: product, variant: variant %}
Note: output is a raw JSON fragment — caller must place it inside the Offer object.
{%- endcomment -%}
{%- assign is_final_sale = false -%}
{%- assign return_days = 30 -%}
{%- if product.tags contains 'final-sale' or product.tags contains 'no-returns' -%}
{%- assign is_final_sale = true -%}
{%- endif -%}
{%- if product.metafields.catalogscan.no_return == 'true' -%}
{%- assign is_final_sale = true -%}
{%- endif -%}
{%- if product.metafields.catalogscan.return_days != blank -%}
{%- assign return_days = product.metafields.catalogscan.return_days -%}
{%- elsif variant.compare_at_price > variant.price -%}
{%- assign return_days = 14 -%}
{%- endif -%}
{%- if is_final_sale -%}
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"name": "Final Sale — No Returns",
"returnPolicyCategory": "https://schema.org/MerchantReturnNotPermitted",
"applicableCountry": "US"
}
{%- else -%}
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"name": "Standard Return Policy",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": {{ return_days }},
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn",
"applicableCountry": "US",
"merchantReturnLink": "https://{{ shop.permanent_domain }}/policies/refund-policy"
}
{%- endif -%}
Step 2 — Include the snippet in your product JSON-LD template
In your product page Liquid (usually sections/main-product.liquid or a snippets/product-schema.liquid), include the snippet inside the Offer object. Make sure to add a comma before the render tag if other Offer properties follow:
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"description": {{ product.description | strip_html | truncate: 5000 | json }},
"image": [{{ product.featured_image | image_url: width: 1200 | prepend: 'https:' | json }}],
"sku": {{ variant.sku | json }},
"offers": {
"@type": "Offer",
"url": {{ canonical_url | json }},
"priceCurrency": {{ cart.currency.iso_code | json }},
"price": {{ variant.price | money_without_currency | remove: ',' | json }},
"availability": {%- if variant.available -%}"https://schema.org/InStock"{%- else -%}"https://schema.org/OutOfStock"{%- endif -%},
"itemCondition": "https://schema.org/NewCondition",
{% render 'product-return-policy', product: product, variant: variant %}
}
}
Step 3 — Add the site-wide policy to your Organization entity
In theme.liquid, within your site-level Organization JSON-LD block, add the default return policy as a hasMerchantReturnPolicy reference. This gives AI agents the policy signal when they parse your homepage or any non-product page:
{
"@context": "https://schema.org",
"@type": "Organization",
"@id": "https://{{ shop.permanent_domain }}/#organization",
"name": {{ shop.name | json }},
"url": "https://{{ shop.permanent_domain }}/",
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"name": "Standard Return Policy",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn",
"applicableCountry": "US",
"merchantReturnLink": "https://{{ shop.permanent_domain }}/policies/refund-policy"
}
}
Five common mistakes that break return policy schema
Putting MerchantReturnPolicy only on Organization, not on each product Offer. This is the most common implementation error. The Organization block on your homepage declares a site-wide policy, but AI shopping agents checking your product page fetch that page in isolation — they do not also fetch the homepage. Without hasMerchantReturnPolicy directly on the product's Offer object, every product page is effectively a "no policy found" signal regardless of what you declared on Organization.
Using a plain text string for returnPolicyCategory instead of a URL. A common mistake is outputting "returnPolicyCategory": "30 days" or "returnPolicyCategory": "MerchantReturnFiniteReturnWindow" without the https://schema.org/ prefix. Schema.org enum properties require full URL values. The bare string form fails validation silently — Google's Rich Results Test will ignore the property, and AI agents cannot map it to the enumerated vocabulary.
Omitting merchantReturnDays when using MerchantReturnFiniteReturnWindow. If you set returnPolicyCategory to MerchantReturnFiniteReturnWindow without also providing merchantReturnDays, the policy is technically invalid — you have declared a finite window without specifying how long it is. Agents and validators treat this as an incomplete policy. Always include the integer day count when using a finite return window.
Not handling final-sale and clearance products separately. If your store sells both returnable and non-returnable products but outputs the same return policy for all of them, you are misrepresenting your policy to AI agents. A shopper who asks "can I return this?" for a final-sale item will get incorrect information surfaced in the AI response. Tag final-sale products explicitly and output MerchantReturnNotPermitted for those SKUs — this is accurate, and accuracy is what builds long-term AI agent trust.
Using returnFees: FreeReturn when you charge for return shipping. Some stores copy example JSON-LD that includes FreeReturn as a boilerplate without checking whether their actual policy is free returns. Using incorrect enum values is worse than omitting the property — an agent that verifies structured data claims against the linked merchantReturnLink page will flag the mismatch as a trust signal failure. Use ReturnShippingFees if the customer pays return shipping, and FreeReturn only if you genuinely cover it.
FAQ
MerchantReturnPolicy schema in its default product JSON-LD block. The default Product JSON-LD includes Offer with price, availability, and SKU, but the hasMerchantReturnPolicy property is entirely absent. AI shopping agents that check structured data for return policy information will find nothing on a default Dawn store and typically assume an unknown or conservative return window — which can disadvantage your products in comparison-shopping responses against competitors who have implemented the schema.MerchantReturnPolicy to your Organization entity in theme.liquid declares a site-wide default policy. Adding it to each individual Offer via hasMerchantReturnPolicy on the product page makes the policy discoverable at the product level — which is what AI shopping agents check when comparing products. For maximum effectiveness, do both: declare the default on Organization and repeat the hasMerchantReturnPolicy on each Offer, using a per-product override for exceptions like final sale items.returnPolicyCategory to https://schema.org/MerchantReturnNotPermitted for any product where returns are not allowed. The cleanest Shopify implementation is to tag final-sale products with a final-sale tag, then use a Liquid conditional in your product JSON-LD snippet: if product.tags contains 'final-sale', output MerchantReturnNotPermitted; otherwise output your standard finite return window. This approach requires no metafield setup — a product tag is enough to trigger the schema override, and you can bulk-tag clearance products from the Shopify admin.returnMethod property accepts a single URL or an array of URLs from the ReturnActionAccessibilityEnumeration vocabulary: ReturnByMail, ReturnInStore, or ReturnAtKiosk. If your store offers multiple return methods, use a JSON array: "returnMethod": ["https://schema.org/ReturnByMail", "https://schema.org/ReturnInStore"]. Google's Rich Results Test validates arrays for returnMethod correctly. Listing both methods signals greater return flexibility to AI shopping agents, which typically surfaces as a positive trust factor in comparison responses.hasMerchantReturnPolicy property on the Offer object and whether the linked MerchantReturnPolicy contains the required returnPolicyCategory and — when applicable — merchantReturnDays. Products missing hasMerchantReturnPolicy are flagged in the return policy signal of the AI readiness report. CatalogScan also detects the common mistake of declaring MerchantReturnPolicy on Organization without linking it to the product-level Offer. Run a free scan to see whether your return policy schema passes the full check.Is your return policy visible to AI shopping agents?
CatalogScan checks hasMerchantReturnPolicy on every product Offer, validates returnPolicyCategory enum values, and flags final-sale override gaps — in 90 seconds, no login.