HomeBlog › Return policy schema

Shopify return policy schema for AI shopping agents: MerchantReturnPolicy, hasMerchantReturnPolicy, and conditional final-sale overrides

June 13, 2026  ·  CatalogScan  ·  MerchantReturnPolicy hasMerchantReturnPolicy Shopify Liquid AI shopping

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.

73%
of Shopify stores have no MerchantReturnPolicy in product JSON-LD
#3
return window ranks as #3 purchase decision factor in AI shopping comparisons
0
default Dawn product templates include hasMerchantReturnPolicy on Offer

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:

  1. Comparison filtering. ChatGPT Shopping and Google AI Mode display return windows alongside price and shipping when presenting product comparison tables. Products without hasMerchantReturnPolicy show "return policy unknown" — a conversion-killing label next to a competitor who has "free 30-day returns" in clean structured data.
  2. Exclusion from curated lists. Agents compiling "top gifts under $50 with easy returns" or similar intent-driven lists programmatically filter for returnPolicyCategory: MerchantReturnFiniteReturnWindow with merchantReturnDays ≥ 30. If your policy is missing, you are excluded from the list regardless of your actual policy.
  3. 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.
The gap is exploitable. Because 73% of Shopify stores don't output 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.

Core properties
returnPolicyCategory
Required
The most important property. A URL from the 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/.
merchantReturnDays
Required if finite window
Integer — the number of days in the return window. Required when 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.
returnMethod
Recommended
How the customer returns the item. Values from ReturnActionAccessibilityEnumeration: ReturnByMail, ReturnInStore, ReturnAtKiosk. Accepts a single URL or an array of URLs if multiple methods are offered.
returnFees
Recommended
Who pays for the return. Values: 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.
applicableCountry
Recommended
Which country this policy applies to. ISO 3166-1 alpha-2 string (e.g., "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.
merchantReturnLink
Recommended
URL of your human-readable return policy page. Provides a fallback for agents that want to surface the full policy text. Use an absolute URL such as https://yourstore.com/policies/refund-policy. Shopify auto-generates this URL for all stores.
name
Optional
Human-readable policy name. Used when multiple policies exist and an agent needs to distinguish them. Example: "Standard 30-Day Policy", "Final Sale — No Returns". Not required for a single-policy store but valuable when you have conditional policies per product.
inStoreReturnsOffered
Optional
Boolean — whether the item can be returned in-store. A convenience property that duplicates what 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:

  1. A site-wide default policy attached to the Organization entity in theme.liquid via the hasMerchantReturnPolicy property.
  2. A per-product policy override on the Offer object in the product page JSON-LD, using a Liquid conditional to substitute the appropriate MerchantReturnPolicy block 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.

Critical gap: Declaring 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.
Missing the Offer link (common mistake)
{
  "@type": "Offer",
  "price": "89.00",
  "priceCurrency": "USD",
  "availability": "https://schema.org/InStock"
  /* hasMerchantReturnPolicy absent */
}
Correct: policy linked on Offer
{
  "@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 -%}

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

Mistake 1

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.

Mistake 2

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.

Mistake 3

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.

Mistake 4

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.

Mistake 5

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

Does Shopify Dawn output MerchantReturnPolicy structured data by default?
No. Shopify Dawn does not output 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.
What is the difference between adding MerchantReturnPolicy to Organization vs. to each Offer?
Adding 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.
How do I mark Shopify products as final sale (no returns) in structured data?
Set 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.
Can I list both mail returns and in-store returns in MerchantReturnPolicy?
Yes. The 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.
Does CatalogScan detect missing MerchantReturnPolicy structured data?
Yes. CatalogScan checks whether your Shopify product pages include a 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.

Run a free scan More guides