Return Policy Schema Framework
Use conditional-logic schema generation to keep MerchantReturnPolicy markup aligned with the return terms you actually publish and avoid stale or invalid static templates.
When To Use It
Google Search CentralReturn Policy documentationPlace return information on a single page that describes the return policy of your business, nested under Organization via hasMerchantReturnPolicy. It acts as the default for your products: a policy on a product's Offer overrides it, and Merchant Center or Search Console settings and the Content API for Shopping take precedence over both.
Key Implementation Documentation Highlights
- Required option: give applicableCountry and returnPolicyCategory together, or merchantReturnLink on its own (the URL of the page that describes the policy to customers).
- applicableCountry and returnPolicyCountry: ISO 3166-1 alpha-2 country codes, at most 50 each. applicableCountry is where the product is sold; returnPolicyCountry is where it has to be sent back to.
- returnPolicyCategory: MerchantReturnFiniteReturnWindow, MerchantReturnNotPermitted or MerchantReturnUnlimitedWindow. A finite window requires merchantReturnDays, counted from the delivery date.
- Recommended properties: merchantReturnDays, returnPolicyCountry, returnMethod, returnFees, returnShippingFeesAmount, returnLabelSource, itemCondition, refundType, restockingFee, the customer remorse and item defect variants, and returnPolicySeasonalOverride.
- Enumerations: returnMethod is ReturnAtKiosk, ReturnByMail or ReturnInStore (one or more); returnFees is FreeReturn, ReturnFeesCustomerResponsibility or ReturnShippingFees; returnLabelSource is ReturnLabelCustomerResponsibility, ReturnLabelDownloadAndPrint or ReturnLabelInBox; refundType is ExchangeRefund, FullRefund or StoreCreditRefund; itemCondition is NewCondition, RefurbishedCondition, UsedCondition or DamagedCondition.
- Fees: specify returnShippingFeesAmount (a MonetaryAmount with an ISO 4217 currency) only when returnFees is ReturnShippingFees. restockingFee as a Number is a percentage of the price paid; as a MonetaryAmount it is a fixed fee.
- Remorse and defect returns: customerRemorseReturnFees, customerRemorseReturnLabelSource and itemDefectReturnFees, itemDefectReturnLabelSource take the same values; their ShippingFeesAmount properties are only needed when that return carries a non-zero shipping fee.
- returnPolicySeasonalOverride: a MerchantReturnPolicySeasonalOverride for holiday or seasonal exceptions. It requires returnPolicyCategory, needs merchantReturnDays for a finite window, and takes startDate and endDate.
- MerchantReturnNotPermitted: return fee, method and day properties are not meaningful, so leave them out.
- Offer-level override: hasMerchantReturnPolicy on a product's Offer overrides the organization-level policy and supports a more limited set of properties (see the merchant listing documentation).
- Precedence: Content API for Shopping, then Merchant Center or Search Console settings, then product-level markup, then organization-level markup.
- Google expects content parity: every marked-up value must match the policy text users can read on the page. Validate with the Rich Results Test and URL Inspection after deployment.
Template Approach
const pageData = {
storeName: "Trailhead Outfitters",
homeUrl: "https://www.example.com/",
country: "US",
returnDays: 30
};
const staticTemplate = {
"@context": "https://schema.org",
"@type": "OnlineStore",
"name": pageData.storeName,
"url": pageData.homeUrl,
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": pageData.country,
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": pageData.returnDays,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
};
{
"@context": "https://schema.org",
"@type": "OnlineStore",
"name": "Trailhead Outfitters",
"url": "https://www.example.com/",
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "US",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail",
"returnFees": "https://schema.org/FreeReturn"
}
}
Conditional-logic Framework
const ISO_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2}))?(Z|[+-]\d{2}:\d{2})?)?$/;
function isAbsoluteUrl(value) {
return typeof value === "string" && /^https?:\/\/[^\s/?#]+\.[^\s/?#]+(?:[/?#]\S*)?$/i.test(value);
}
// Returns the ISO 8601 string unchanged (timezone offset kept), or null if unparseable.
function toIsoDateTime(value) {
if (typeof value !== "string") return null;
const text = value.trim();
const m = text.match(ISO_DATE_TIME);
if (!m) return null;
const [, y, mo, d, h = "00", mi = "00", s = "00"] = m;
const date = new Date(Date.UTC(+y, +mo - 1, +d));
if (date.getUTCMonth() !== +mo - 1 || date.getUTCDate() !== +d) return null;
if (+h > 23 || +mi > 59 || +s > 59) return null;
return text;
}
function toIsoDate(value) {
const iso = toIsoDateTime(value);
return iso ? iso.slice(0, 10) : null;
}
// Drops null, undefined, "", [] and {} recursively.
function compact(value) {
if (Array.isArray(value)) {
const items = value.map(compact).filter((v) => v !== undefined);
return items.length ? items : undefined;
}
if (value && typeof value === "object") {
const out = {};
for (const [key, v] of Object.entries(value)) {
const c = compact(v);
if (c !== undefined) out[key] = c;
}
return Object.keys(out).length ? out : undefined;
}
return value === null || value === undefined || value === "" ? undefined : value;
}
function passesPageGates(source) {
if (source.indexable === false) return false;
if (source.canonicalUrl && source.url && source.canonicalUrl !== source.url) return false;
return source.contentVisible !== false;
}
const S = "https://schema.org/";
const ORGANIZATION_TYPES = { online_store: "OnlineStore", online_business: "OnlineBusiness", corporation: "Corporation" };
const RETURN_CATEGORY = {
finite: S + "MerchantReturnFiniteReturnWindow",
not_permitted: S + "MerchantReturnNotPermitted",
unlimited: S + "MerchantReturnUnlimitedWindow"
};
const RETURN_FEES = { free: S + "FreeReturn", customer_pays: S + "ReturnFeesCustomerResponsibility", shipping_fee: S + "ReturnShippingFees" };
const RETURN_METHOD = { kiosk: S + "ReturnAtKiosk", mail: S + "ReturnByMail", in_store: S + "ReturnInStore" };
const RETURN_LABEL = { customer: S + "ReturnLabelCustomerResponsibility", download: S + "ReturnLabelDownloadAndPrint", in_box: S + "ReturnLabelInBox" };
const REFUND_TYPE = { exchange: S + "ExchangeRefund", full: S + "FullRefund", store_credit: S + "StoreCreditRefund" };
const ITEM_CONDITION = { new: S + "NewCondition", refurbished: S + "RefurbishedCondition", used: S + "UsedCondition", damaged: S + "DamagedCondition" };
function chooseOrganizationType(org) {
return ORGANIZATION_TYPES[org.kind] || "Organization";
}
function toCountryCode(value) {
const code = String(value || "").toUpperCase();
return /^[A-Z]{2}$/.test(code) ? code : null;
}
// ISO 3166-1 alpha-2 codes, at most 50; null when any code is invalid or the list is too long.
function toCountryList(values) {
const codes = (values || []).map(toCountryCode);
if (codes.includes(null) || codes.length > 50) return null;
return [...new Set(codes)];
}
function toWholeNumber(value) {
return Number.isInteger(value) && value >= 0 ? value : null;
}
function mapEnum(keys, table) {
return (keys || []).map((k) => table[k]).filter(Boolean);
}
function buildMoney(amount) {
const currency = String((amount && amount.currency) || "").toUpperCase();
if (!amount || typeof amount.value !== "number" || amount.value < 0 || !/^[A-Z]{3}$/.test(currency)) return null;
return { "@type": "MonetaryAmount", value: amount.value, currency };
}
// restockingFee: a Number is a percentage of the price paid, a MonetaryAmount is a fixed fee.
function buildRestockingFee(fee) {
if (!fee) return null;
if (typeof fee.percent === "number") return fee.percent >= 0 && fee.percent <= 100 ? fee.percent : null;
return buildMoney(fee);
}
// Fee type and label source; the shipping fee amount only (and always) with ReturnShippingFees.
function buildFees(group) {
if (!group) return {};
const amount = buildMoney(group.shippingFee);
let fees = RETURN_FEES[group.fees] || null;
if (fees === RETURN_FEES.shipping_fee && !amount) fees = null;
return { fees, label: RETURN_LABEL[group.label] || null, amount: fees === RETURN_FEES.shipping_fee ? amount : null };
}
function buildSeasonalOverride(season) {
const category = RETURN_CATEGORY[season.category];
if (!category) return null; // returnPolicyCategory is required in an override
const days = toWholeNumber(season.days);
if (category === RETURN_CATEGORY.finite && days === null) return null;
const startDate = toIsoDate(season.start);
const endDate = toIsoDate(season.end);
if (!startDate || !endDate || endDate < startDate) return null; // a dated window, end on or after start
return {
"@type": "MerchantReturnPolicySeasonalOverride",
startDate,
endDate,
returnPolicyCategory: category,
merchantReturnDays: category === RETURN_CATEGORY.finite ? days : null
};
}
function buildReturnPolicy(policy) {
const link = isAbsoluteUrl(policy.link) ? policy.link : null;
const countries = toCountryList(policy.countries);
const category = RETURN_CATEGORY[policy.category] || null;
if (countries === null) return null; // invalid code or more than 50 countries
// Required option: applicableCountry + returnPolicyCategory, or merchantReturnLink alone.
if (!(countries.length && category)) {
return link ? { "@type": "MerchantReturnPolicy", merchantReturnLink: link } : null;
}
const days = toWholeNumber(policy.days);
if (category === RETURN_CATEGORY.finite && days === null) return null; // finite window needs merchantReturnDays
const base = { "@type": "MerchantReturnPolicy", applicableCountry: countries, returnPolicyCategory: category, merchantReturnLink: link };
// No returns: fees, methods and days are not meaningful.
if (category === RETURN_CATEGORY.not_permitted) return base;
const main = buildFees(policy);
const remorse = buildFees(policy.customerRemorse);
const defect = buildFees(policy.itemDefect);
return {
...base,
merchantReturnDays: category === RETURN_CATEGORY.finite ? days : null,
returnPolicyCountry: toCountryList(policy.returnToCountries),
returnMethod: mapEnum(policy.methods, RETURN_METHOD),
returnFees: main.fees,
returnShippingFeesAmount: main.amount,
returnLabelSource: main.label,
refundType: mapEnum(policy.refundTypes, REFUND_TYPE),
itemCondition: mapEnum(policy.conditions, ITEM_CONDITION),
restockingFee: buildRestockingFee(policy.restockingFee),
customerRemorseReturnFees: remorse.fees,
customerRemorseReturnShippingFeesAmount: remorse.amount,
customerRemorseReturnLabelSource: remorse.label,
itemDefectReturnFees: defect.fees,
itemDefectReturnShippingFeesAmount: defect.amount,
itemDefectReturnLabelSource: defect.label,
returnPolicySeasonalOverride: (policy.seasonalOverrides || []).map(buildSeasonalOverride)
};
}
function buildReturnPolicySchema(source) {
// (a) Page gates: indexable, self-canonical, visible policy text, on the page that describes the return policy.
if (!passesPageGates(source) || source.pageRole !== "return_policy") return null;
// (b) At least one policy must pass Google's required option (country + category, or merchantReturnLink).
const hasMerchantReturnPolicy = (source.policies || []).map(buildReturnPolicy).filter(Boolean);
if (!hasMerchantReturnPolicy.length) return null;
// House rule: nest under the Organization, identified by name, home URL and the same @id as the home page markup.
const org = source.organization || {};
if (!org.name || !isAbsoluteUrl(org.url)) return null;
// (c) and (d) applied inside buildReturnPolicy; (e) strip empty values.
return compact({
"@context": "https://schema.org",
"@type": chooseOrganizationType(org),
"@id": `${org.url}#organization`,
name: org.name,
url: org.url,
hasMerchantReturnPolicy
});
}
// Usage:
// const source = {
// "url": "https://www.example.com/policies/returns",
// "canonicalUrl": "https://www.example.com/policies/returns",
// "indexable": true,
// "contentVisible": true,
// "pageRole": "return_policy",
// "organization": {
// "kind": "online_store",
// "name": "Trailhead Outfitters",
// "url": "https://www.example.com/"
// },
// "policies": [
// {
// "countries": [
// "US",
// "CA"
// ],
// "returnToCountries": [
// "US"
// ],
// "category": "finite",
// "days": 30,
// "link": "https://www.example.com/policies/returns",
// "methods": [
// "mail",
// "in_store"
// ],
// "fees": "shipping_fee",
// "shippingFee": {
// "value": 6.95,
// "currency": "USD"
// },
// "label": "download",
// "refundTypes": [
// "full",
// "store_credit"
// ],
// "conditions": [
// "new"
// ],
// "restockingFee": {
// "percent": 0
// },
// "customerRemorse": {
// "fees": "shipping_fee",
// "shippingFee": {
// "value": 6.95,
// "currency": "USD"
// },
// "label": "download"
// },
// "itemDefect": {
// "fees": "free",
// "label": "in_box"
// },
// "seasonalOverrides": [
// {
// "category": "finite",
// "days": 60,
// "start": "2026-11-20",
// "end": "2026-12-31"
// }
// ]
// }
// ]
// };
// const schema = buildReturnPolicySchema(source); // null when a gate or required property fails
import re
from datetime import date, datetime, timezone
ISO_DATE_TIME = re.compile(r"^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2}))?(Z|[+-]\d{2}:\d{2})?)?$")
ABSOLUTE_URL = re.compile(r"^https?://[^\s/?#]+\.[^\s/?#]+(?:[/?#]\S*)?$", re.I)
def is_absolute_url(value):
return isinstance(value, str) and bool(ABSOLUTE_URL.match(value))
# Returns the ISO 8601 string unchanged (timezone offset kept), or None if unparseable.
def to_iso_date_time(value):
if not isinstance(value, str):
return None
text = value.strip()
m = ISO_DATE_TIME.match(text)
if not m:
return None
y, mo, d = int(m[1]), int(m[2]), int(m[3])
try:
date(y, mo, d)
except ValueError:
return None
if int(m[4] or 0) > 23 or int(m[5] or 0) > 59 or int(m[6] or 0) > 59:
return None
return text
def to_iso_date(value):
iso = to_iso_date_time(value)
return iso[:10] if iso else None
# Drops None, "", [] and {} recursively.
def compact(value):
if isinstance(value, list):
items = [c for c in (compact(v) for v in value) if c is not None]
return items or None
if isinstance(value, dict):
out = {k: c for k, c in ((k, compact(v)) for k, v in value.items()) if c is not None}
return out or None
return None if value is None or value == "" else value
def passes_page_gates(source):
if source.get("indexable") is False:
return False
if source.get("canonicalUrl") and source.get("url") and source["canonicalUrl"] != source["url"]:
return False
return source.get("contentVisible") is not False
S = "https://schema.org/"
ORGANIZATION_TYPES = {"online_store": "OnlineStore", "online_business": "OnlineBusiness", "corporation": "Corporation"}
RETURN_CATEGORY = {
"finite": S + "MerchantReturnFiniteReturnWindow",
"not_permitted": S + "MerchantReturnNotPermitted",
"unlimited": S + "MerchantReturnUnlimitedWindow",
}
RETURN_FEES = {"free": S + "FreeReturn", "customer_pays": S + "ReturnFeesCustomerResponsibility", "shipping_fee": S + "ReturnShippingFees"}
RETURN_METHOD = {"kiosk": S + "ReturnAtKiosk", "mail": S + "ReturnByMail", "in_store": S + "ReturnInStore"}
RETURN_LABEL = {"customer": S + "ReturnLabelCustomerResponsibility", "download": S + "ReturnLabelDownloadAndPrint", "in_box": S + "ReturnLabelInBox"}
REFUND_TYPE = {"exchange": S + "ExchangeRefund", "full": S + "FullRefund", "store_credit": S + "StoreCreditRefund"}
ITEM_CONDITION = {"new": S + "NewCondition", "refurbished": S + "RefurbishedCondition", "used": S + "UsedCondition", "damaged": S + "DamagedCondition"}
def choose_organization_type(org):
return ORGANIZATION_TYPES.get(org.get("kind"), "Organization")
def to_country_code(value):
code = str(value or "").upper()
return code if re.fullmatch(r"[A-Z]{2}", code) else None
# ISO 3166-1 alpha-2 codes, at most 50; None when any code is invalid or the list is too long.
def to_country_list(values):
codes = [to_country_code(v) for v in values or []]
if None in codes or len(codes) > 50:
return None
return list(dict.fromkeys(codes))
def to_whole_number(value):
return value if isinstance(value, int) and not isinstance(value, bool) and value >= 0 else None
def is_number(value):
return isinstance(value, (int, float)) and not isinstance(value, bool)
def map_enum(keys, table):
return [table[k] for k in keys or [] if k in table]
def build_money(amount):
currency = str((amount or {}).get("currency") or "").upper()
if not amount or not is_number(amount.get("value")) or amount["value"] < 0 or not re.fullmatch(r"[A-Z]{3}", currency):
return None
return {"@type": "MonetaryAmount", "value": amount["value"], "currency": currency}
# restockingFee: a Number is a percentage of the price paid, a MonetaryAmount is a fixed fee.
def build_restocking_fee(fee):
if not fee:
return None
if is_number(fee.get("percent")):
return fee["percent"] if 0 <= fee["percent"] <= 100 else None
return build_money(fee)
# Fee type and label source; the shipping fee amount only (and always) with ReturnShippingFees.
def build_fees(group):
if not group:
return {}
amount = build_money(group.get("shippingFee"))
fees = RETURN_FEES.get(group.get("fees"))
if fees == RETURN_FEES["shipping_fee"] and not amount:
fees = None
return {"fees": fees, "label": RETURN_LABEL.get(group.get("label")), "amount": amount if fees == RETURN_FEES["shipping_fee"] else None}
def build_seasonal_override(season):
category = RETURN_CATEGORY.get(season.get("category"))
if not category:
return None # returnPolicyCategory is required in an override
days = to_whole_number(season.get("days"))
if category == RETURN_CATEGORY["finite"] and days is None:
return None
start_date, end_date = to_iso_date(season.get("start")), to_iso_date(season.get("end"))
if not start_date or not end_date or end_date < start_date:
return None # a dated window, end on or after start
return {
"@type": "MerchantReturnPolicySeasonalOverride",
"startDate": start_date,
"endDate": end_date,
"returnPolicyCategory": category,
"merchantReturnDays": days if category == RETURN_CATEGORY["finite"] else None,
}
def build_return_policy(policy):
link = policy.get("link") if is_absolute_url(policy.get("link")) else None
countries = to_country_list(policy.get("countries"))
category = RETURN_CATEGORY.get(policy.get("category"))
if countries is None:
return None # invalid code or more than 50 countries
# Required option: applicableCountry + returnPolicyCategory, or merchantReturnLink alone.
if not (countries and category):
return {"@type": "MerchantReturnPolicy", "merchantReturnLink": link} if link else None
days = to_whole_number(policy.get("days"))
if category == RETURN_CATEGORY["finite"] and days is None:
return None # finite window needs merchantReturnDays
base = {"@type": "MerchantReturnPolicy", "applicableCountry": countries, "returnPolicyCategory": category, "merchantReturnLink": link}
# No returns: fees, methods and days are not meaningful.
if category == RETURN_CATEGORY["not_permitted"]:
return base
main = build_fees(policy)
remorse = build_fees(policy.get("customerRemorse"))
defect = build_fees(policy.get("itemDefect"))
return {
**base,
"merchantReturnDays": days if category == RETURN_CATEGORY["finite"] else None,
"returnPolicyCountry": to_country_list(policy.get("returnToCountries")),
"returnMethod": map_enum(policy.get("methods"), RETURN_METHOD),
"returnFees": main.get("fees"),
"returnShippingFeesAmount": main.get("amount"),
"returnLabelSource": main.get("label"),
"refundType": map_enum(policy.get("refundTypes"), REFUND_TYPE),
"itemCondition": map_enum(policy.get("conditions"), ITEM_CONDITION),
"restockingFee": build_restocking_fee(policy.get("restockingFee")),
"customerRemorseReturnFees": remorse.get("fees"),
"customerRemorseReturnShippingFeesAmount": remorse.get("amount"),
"customerRemorseReturnLabelSource": remorse.get("label"),
"itemDefectReturnFees": defect.get("fees"),
"itemDefectReturnShippingFeesAmount": defect.get("amount"),
"itemDefectReturnLabelSource": defect.get("label"),
"returnPolicySeasonalOverride": [build_seasonal_override(s) for s in policy.get("seasonalOverrides") or []],
}
def build_return_policy_schema(source):
# (a) Page gates: indexable, self-canonical, visible policy text, on the page that describes the return policy.
if not passes_page_gates(source) or source.get("pageRole") != "return_policy":
return None
# (b) At least one policy must pass Google's required option (country + category, or merchantReturnLink).
policies = [p for p in (build_return_policy(p) for p in source.get("policies") or []) if p]
if not policies:
return None
# House rule: nest under the Organization, identified by name, home URL and the same @id as the home page markup.
org = source.get("organization") or {}
if not org.get("name") or not is_absolute_url(org.get("url")):
return None
# (c) and (d) applied inside build_return_policy; (e) strip empty values.
return compact({
"@context": "https://schema.org",
"@type": choose_organization_type(org),
"@id": f"{org['url']}#organization",
"name": org["name"],
"url": org["url"],
"hasMerchantReturnPolicy": policies,
})
{
"@context": "https://schema.org",
"@type": "OnlineStore",
"@id": "https://www.example.com/#organization",
"name": "Trailhead Outfitters",
"url": "https://www.example.com/",
"hasMerchantReturnPolicy": [
{
"@type": "MerchantReturnPolicy",
"applicableCountry": [
"US",
"CA"
],
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnLink": "https://www.example.com/policies/returns",
"merchantReturnDays": 30,
"returnPolicyCountry": [
"US"
],
"returnMethod": [
"https://schema.org/ReturnByMail",
"https://schema.org/ReturnInStore"
],
"returnFees": "https://schema.org/ReturnShippingFees",
"returnShippingFeesAmount": {
"@type": "MonetaryAmount",
"value": 6.95,
"currency": "USD"
},
"returnLabelSource": "https://schema.org/ReturnLabelDownloadAndPrint",
"refundType": [
"https://schema.org/FullRefund",
"https://schema.org/StoreCreditRefund"
],
"itemCondition": [
"https://schema.org/NewCondition"
],
"restockingFee": 0,
"customerRemorseReturnFees": "https://schema.org/ReturnShippingFees",
"customerRemorseReturnShippingFeesAmount": {
"@type": "MonetaryAmount",
"value": 6.95,
"currency": "USD"
},
"customerRemorseReturnLabelSource": "https://schema.org/ReturnLabelDownloadAndPrint",
"itemDefectReturnFees": "https://schema.org/FreeReturn",
"itemDefectReturnLabelSource": "https://schema.org/ReturnLabelInBox",
"returnPolicySeasonalOverride": [
{
"@type": "MerchantReturnPolicySeasonalOverride",
"startDate": "2026-11-20",
"endDate": "2026-12-31",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 60
}
]
}
]
}
Why Conditional Logic Is Better Than Static Templates
- Enforces the required option in code: applicableCountry plus returnPolicyCategory, or merchantReturnLink alone, and never a policy with neither.
- Drops fee, method and day properties when returns are not permitted, and blocks a finite window that has no merchantReturnDays.
- Keeps returnShippingFeesAmount tied to ReturnShippingFees, so a fee change in the policy config cannot leave a stale amount behind.
- Emits seasonal overrides only with a valid dated window, so a lapsed holiday policy is never published as all-year.
- Reads from the same policy config that renders the page and feeds Merchant Center, which keeps markup, visible text and higher-precedence settings in sync.
Property Reference
What Google's Merchant return policy (organization-level) documentation requires and recommends, property by property. These are the same rules the SchemaCDN validator checks. Each name links to its schema.org definition.
| Property | Expects | Notes | |
|---|---|---|---|
| applicableCountry | Required | Text, can repeat | The country code(s) the return policy applies to (where the product is sold and returned from) |
| returnPolicyCategory | Required | Enum | The type of return policy. One of: MerchantReturnFiniteReturnWindow, MerchantReturnNotPermitted, MerchantReturnUnlimitedWindow. |
| merchantReturnLink | Required | URL | The URL of a web page that describes the return policy to your customers. |
| merchantReturnDays | Recommended | Integer | The number of days from the delivery date that a product can be returned. |
| returnPolicyCountry | Recommended | Text, can repeat | The country where the product has to be sent to for returns. |
| returnMethod | Recommended | Enum, can repeat | The type of return method offered; one or more values. One of: ReturnAtKiosk, ReturnByMail, ReturnInStore. |
| returnFees | Recommended | Enum | The type of return fees. One of: FreeReturn, ReturnFeesCustomerResponsibility, ReturnShippingFees. |
| returnShippingFeesAmount | Recommended | MonetaryAmount | The cost of shipping for returning a product. |
| returnShippingFeesAmount.currency | Recommended | Text | ISO 4217 currency code. |
| returnShippingFeesAmount.value | Recommended | Number | The amount. |
| returnLabelSource | Recommended | Enum | How the customer obtains a return shipping label. One of: ReturnLabelCustomerResponsibility, ReturnLabelDownloadAndPrint, ReturnLabelInBox. |
| itemCondition | Recommended | Enum, can repeat | Acceptable condition(s) of returned items. One of: DamagedCondition, NewCondition, RefurbishedCondition, UsedCondition. |
| refundType | Recommended | Enum, can repeat | The type of refund available. One of: ExchangeRefund, FullRefund, StoreCreditRefund. |
| restockingFee | Recommended | MonetaryAmount or Number | The restocking fee charged for returns. |
| customerRemorseReturnFees | Recommended | Enum | Return fee type if the product is returned due to customer remorse. One of: FreeReturn, ReturnFeesCustomerResponsibility, ReturnShippingFees. |
| customerRemorseReturnLabelSource | Recommended | Enum | Return label source for customer remorse returns. One of: ReturnLabelCustomerResponsibility, ReturnLabelDownloadAndPrint, ReturnLabelInBox. |
| customerRemorseReturnShippingFeesAmount | Recommended | MonetaryAmount | Shipping fee for customer remorse returns. |
| customerRemorseReturnShippingFeesAmount.currency | Recommended | Text | ISO 4217 currency code. |
| customerRemorseReturnShippingFeesAmount.value | Recommended | Number | The amount. |
| itemDefectReturnFees | Recommended | Enum | Return fee type for defect products. One of: FreeReturn, ReturnFeesCustomerResponsibility, ReturnShippingFees. |
| itemDefectReturnLabelSource | Recommended | Enum | Return label source for defect product returns. One of: ReturnLabelCustomerResponsibility, ReturnLabelDownloadAndPrint, ReturnLabelInBox. |
| itemDefectReturnShippingFeesAmount | Recommended | MonetaryAmount | Shipping fee for defect product returns. |
| itemDefectReturnShippingFeesAmount.currency | Recommended | Text | ISO 4217 currency code. |
| itemDefectReturnShippingFeesAmount.value | Recommended | Number | The amount. |
| returnPolicySeasonalOverride | Recommended | MerchantReturnPolicySeasonalOverride, can repeat | A seasonal override of the return policy. |
| returnPolicySeasonalOverride.returnPolicyCategory | Required | Enum | The type of return policy for the season. One of: MerchantReturnFiniteReturnWindow, MerchantReturnNotPermitted, MerchantReturnUnlimitedWindow. |
| returnPolicySeasonalOverride.merchantReturnDays | Recommended | Integer | Number of return days during the season. |
| returnPolicySeasonalOverride.startDate | Recommended | Date or DateTime | Start of the seasonal override. |
| returnPolicySeasonalOverride.endDate | Recommended | Date or DateTime | End of the seasonal override. |