Skip to main content

Product disclosures

Product disclosures let merchants attach regulatory safety and compliance content — such as California Proposition 65 warnings or CPSC choking-hazard warnings — to products as structured data instead of free text buried in descriptions, tags, or theme code.

Because disclosures are structured, the same content renders consistently across the Online Store, the Shop app, custom storefronts, checkout, and agentic channels, and it can be syndicated to marketplaces without re-entry. Disclosure copy is preserved verbatim, which is what regulators and agents require.

This guide covers every surface where you can manage disclosures:

Note

Disclosures carry legal weight and are distinct from informational labels ("final sale", "limited edition"). The cost of a missing disclosure (legal exposure, channel delisting) is far higher than an unnecessary one, so over-disclosure is the safe default. Merchants own compliance liability; Shopify provides the tooling.


A product disclosure is composed of three layers:

The three layers of a product disclosure
LayerWhat it is
Disclosure taxonomyAn open, Shopify-governed list of disclosure types published in the public product-taxonomy repo. Each leaf (for example, Prop 65 cancer warning) has a stable ID exposed as gid://shopify/TaxonomyDisclosure/<id>, with default title, content, jurisdictions, legal citation, and symbol. Disclosure is a separate axis of the taxonomy, parallel to product categories, not a child of a category.
Disclosure metaobjectsShop-scoped metaobject entries that hold the disclosure content shown to buyers. Shopify seeds canonical entries from the taxonomy, and merchants can edit them or author custom entries. Multiple entries of the same type are allowed (content can vary per product).
Product metafieldA standard product metafield, shopify.disclosure of type list.disclosure_reference, that holds the list of disclosure metaobjects applied to a product.

Anchor to The product metafieldThe product metafield

PropertyValue
Namespaceshopify
Keydisclosure
Typelist.disclosure_reference
Owner typeProduct
Storefront API visibleYes

disclosure_reference is a dedicated metafield type for disclosure metaobjects. It exists as its own type because disclosures have behavior generic references don't: jurisdiction-aware contextualization, native first-party channel rendering, and tailored Admin UX.

Anchor to Disclosure metaobject definitions and entry typesDisclosure metaobject definitions and entry types

Shopify provides standard metaobject definitions, one per disclosure leaf. The metaobject type is the stable, machine-readable identifier and uses a reserved shopify-- prefix so merchant-authored entries can never collide with Shopify-managed ones.

Shopify provides these entry types:

GroupEntry types
Chemical exposure (Prop 65)shopify--disclosure-us-ca-prop65-cancer, shopify--disclosure-us-ca-prop65-reproductive, shopify--disclosure-us-ca-prop65-cancer_reproductive, shopify--disclosure-us-ca-prop65-alcohol
Choking hazards (CPSC)shopify--disclosure-us-cpsc-choking_small_parts, shopify--disclosure-us-cpsc-choking_balloons, shopify--disclosure-us-cpsc-choking_marbles, shopify--disclosure-us-cpsc-choking_small_balls
Customshopify--disclosure-custom

Each entry exposes these fields:

FieldTypeRequiredDescription
taxonomy_referenceproduct_taxonomy_disclosure_referenceYes (not on custom)Reference to the taxonomy leaf this entry instantiates, set as its GID (for example gid://shopify/TaxonomyDisclosure/<id>). It's a dedicated reference type, not free text; it disambiguates sub-leaves within a disclosure group.
titlesingle line text (max 100)YesBuyer-facing heading (for example, "Choking Hazard").
internal_labelsingle line text (max 255)YesInternal-only label that mirrors the disclosure name. Not exposed to the Storefront API.
contentrich textNoThe disclosure copy, rendered verbatim. Stored as a rich-text JSON document.
symbolfile reference (Image)NoThe visual symbol (for example, the Prop 65 triangle or ISO 7010 W001), stored as a reference to an image media file. For taxonomized disclosures it's populated automatically from the taxonomy when the entry is created.
jurisdictionslist of single line text (min 1)YesJurisdiction codes the disclosure applies to, for example ["US-CA"].
display_preferencesJSONYesWhich surfaces to render on. The surfaces array accepts product_page, cart, and checkout, for example {"surfaces":["product_page"]}.
Note

Custom disclosures (shopify--disclosure-custom) are the sanctioned escape hatch for content Shopify doesn't yet standardize. They have no taxonomy_reference because there's no leaf to point at. They still require title, internal_label, jurisdictions, and display_preferences; capture jurisdictions directly on the entry.

Disclosures are stored at the product grain. They are not stored per variant; variant-specific cases are handled by over-disclosing at the product level or surfacing variant messages in checkout. When surfaced through the Catalog Lookup API, product-grain disclosures are transformed to the variant grain (see agentic channels).


To work with product disclosures, you need:

  • An app that can make authenticated requests to the GraphQL Admin API.
  • The write_products and write_metaobjects access scopes.
  • To read disclosures from a custom storefront, a Storefront API access token with the unauthenticated_read_product_listings and unauthenticated_read_metaobjects access scopes.

Anchor to Manage disclosures with the Admin APIManage disclosures with the Admin API

For product disclosures, use a three-step API flow: create the disclosure metaobject instances, enable the product disclosure metafield definition, then set the metafield value on the product. You can do everything below in the Admin GraphQL API, or perform the equivalent steps in the product details page in the Shopify admin.

Creating a standard disclosure metaobject auto-enables its metaobject definition, so there's no separate step to enable definitions. Enabling the shopify.disclosure metafield definition is separate: it creates the product-level slot but doesn't create any metaobjects or attach them to a product.

Anchor to Step 1: Create the disclosure metaobjectStep 1: Create the disclosure metaobject

Create a disclosure entry with metaobjectCreate. If the shop hasn't enabled the standard metaobject definition for that type, this call enables it before creating the entry. title, internal_label, jurisdictions, and display_preferences are required (plus taxonomy_reference on non-custom types); content is optional. For taxonomized disclosures, the symbol is populated automatically from the taxonomy when the entry is created, so you don't set it. The content field is a rich-text JSON document so the copy renders verbatim. Repeat the call for each disclosure you need to create.

mutation CreateDisclosureMetaobject($metaobject: MetaobjectCreateInput!) {
metaobjectCreate(metaobject: $metaobject) {
metaobject {
id
handle
type
fields { key value }
}
userErrors { field message code }
}
}
{
"metaobject": {
"type": "shopify--disclosure-us-ca-prop65-cancer",
"handle": "us-ca-prop65-cancer-default",
"fields": [
{ "key": "taxonomy_reference", "value": "gid://shopify/TaxonomyDisclosure/<id>" },
{ "key": "title", "value": "Prop 65 Cancer Warning" },
{ "key": "internal_label", "value": "Prop 65 Cancer Warning" },
{ "key": "content", "value": "{\"type\":\"root\",\"children\":[{\"type\":\"paragraph\",\"children\":[{\"type\":\"text\",\"value\":\"WARNING:\",\"bold\":true},{\"type\":\"text\",\"value\":\" Cancer - http://www.P65Warnings.ca.gov\"}]}]}" },
{ "key": "jurisdictions", "value": "[\"US-CA\"]" },
{ "key": "display_preferences", "value": "{\"surfaces\":[\"product_page\"]}" }
]
}
}

Save the returned metaobject.id; you reference it in Step 3.

Note

Valid TaxonomyDisclosure IDs are 2–5 and 13–16. The handle is merchant-visible and useful as a stable mapping key for CSV import/export and PIM integrations.

Anchor to Step 2: Enable the product disclosure metafield definitionStep 2: Enable the product disclosure metafield definition

Enable the standard shopify.disclosure metafield definition on products with standardMetafieldDefinitionEnable. This creates the product-level metafield definition; it doesn't attach any values to a product.

mutation EnableProductDisclosureMetafieldDefinition {
standardMetafieldDefinitionEnable(
ownerType: PRODUCT
namespace: "shopify"
key: "disclosure"
) {
createdDefinition {
id
namespace
key
ownerType
type { name }
}
userErrors { field message }
}
}

Anchor to Step 3: Set the product metafield valueStep 3: Set the product metafield value

Set the shopify.disclosure metafield to a JSON list of the disclosure metaobject GIDs returned in Step 1.

mutation SetProductDisclosure($productId: ID!, $value: String!) {
metafieldsSet(metafields: [{
ownerId: $productId
namespace: "shopify"
key: "disclosure"
type: "list.disclosure_reference"
value: $value
}]) {
metafields { id namespace key type value }
userErrors { field message code }
}
}
{
"productId": "gid://shopify/Product/<id>",
"value": "[\"gid://shopify/Metaobject/<id>\"]"
}

Anchor to Reading disclosures in the Admin APIReading disclosures in the Admin API

Query the product metafield and dereference the metaobject fields:

{
product(id: "gid://shopify/Product/<id>") {
metafield(namespace: "shopify", key: "disclosure") {
references(first: 10) {
nodes {
... on Metaobject {
id
type
field(key: "title") { value }
field(key: "content") { value }
field(key: "symbol") { value }
field(key: "jurisdictions") { value }
}
}
}
}
}
}

To remove disclosure support, delete the metafield definition (optionally removing associated metafields), then delete the metaobject definitions.

mutation DeleteDisclosureMetafieldDefinition {
metafieldDefinitionDelete(
id: "gid://shopify/MetafieldDefinition/<id>"
deleteAllAssociatedMetafields: true
) {
deletedDefinitionId
userErrors { field message code }
}
}
mutation DeleteDisclosureMetaobjectDefinitions {
prop65Cancer: metaobjectDefinitionDelete(id: "gid://shopify/MetaobjectDefinition/<id>") {
deletedId
userErrors { field message code }
}
}

  • Bulk editor: you can apply taxonomy disclosures to products in bulk through the standard mixed-reference bulk-edit flow (single column).
  • CSV import/export: disclosures are imported and exported by metaobject handle, the same as other mixed-reference metafields. Use a stable handle so external systems (for example, a PIM) can map to the disclosure consistently.

Anchor to Read disclosures with the Storefront APIRead disclosures with the Storefront API

Custom storefronts read disclosures by dereferencing the product metafield's metaobject references.

query ProductDisclosures($handle: String!) {
product(handle: $handle) {
metafield(namespace: "shopify", key: "disclosure") {
references(first: 10) {
nodes {
... on Metaobject {
type
title: field(key: "title") { value }
content: field(key: "content") { value }
symbol: field(key: "symbol") {
reference {
... on MediaImage { image { url altText } }
}
}
jurisdiction: field(key: "jurisdictions") { value }
}
}
}
}
}
}

Two of these fields aren't plain strings, so render them accordingly:

  • content comes back as a rich-text JSON document, not HTML or plain text. Render it through a rich-text renderer (parse the JSON and map its nodes); don't print the raw value, or buyers will see JSON.
  • symbol is a file reference. Dereference it through reference { ... on MediaImage { image { url altText } } } to get a usable image URL. The field's raw value is only the MediaImage GID, which won't render as an image.

Filter by jurisdictions against the buyer's market if you want jurisdiction-aware display; the API returns all disclosures applied to the product.


Anchor to Display disclosures in a themeDisplay disclosures in a theme

On the Online Store, disclosures render through a self-contained Liquid section that reads the shopify.disclosure metafield. It renders only when a product has disclosures attached, so it's safe to include in every product template. The section shows disclosures in a collapsible accordion: collapsed shows all titles on one line separated by bullets; expanded shows each disclosure's full content.

  1. In your Shopify admin, go to Online Store > Themes > Edit code.
  2. Under Sections, click Add a new section and name it disclosures.
  3. Replace the contents with the section below.
{%- assign disclosures = product.metafields.shopify.disclosure.value -%}

{%- if disclosures != blank -%}
<div style="padding: 2rem 1.5rem;">
{%- if section.settings.heading != blank -%}
<h3 style="margin-bottom: 2rem;">{{ section.settings.heading | escape }}</h3>
{%- endif -%}

<details class="disclosures__details"{% if section.settings.open_by_default %} open{% endif %}>
<summary class="disclosures__summary">
{%- for disclosure in disclosures -%}
<span class="disclosures-summary-item">
{%- if disclosure.symbol != blank -%}
<img src="{{ disclosure.symbol.value | image_url: width: 20 }}" alt="" width="20" height="20" loading="lazy">
{%- endif -%}
<span>{{ disclosure.title.value }}</span>
</span>
{%- unless forloop.last -%}<span class="disclosures-separator">&bull;</span>{%- endunless -%}
{%- endfor -%}
</summary>
<div class="disclosures__content">
{%- for disclosure in disclosures -%}
<div class="disclosures-item">
<div class="disclosures-item__header">
{%- if disclosure.symbol != blank -%}
<img src="{{ disclosure.symbol.value | image_url: width: 20 }}" alt="" width="20" height="20" loading="lazy">
{%- endif -%}
{%- if disclosure.title.value != blank -%}<span>{{ disclosure.title.value }}</span>{%- endif -%}
</div>
<div class="disclosures-item__content">{{ disclosure.content | metafield_tag }}</div>
</div>
{%- endfor -%}
</div>
</details>
</div>
{%- endif -%}

{% schema %}
{
"name": "Disclosures",
"limit": 1,
"settings": [
{ "type": "text", "id": "heading", "label": "Heading" },
{ "type": "checkbox", "id": "open_by_default", "label": "Open by default", "default": false }
],
"presets": [{ "name": "Disclosures" }]
}
{% endschema %}
Note

This sample omits the accordion CSS/JS for brevity. Style the accordion to span the full page width — disclosure content needs room to be readable.

How you render depends on your template format.

  • JSON templates (Online Store 2.0): open the theme editor on your product template, click Add section, and select Disclosures. The section supports a Heading setting (optional heading above the accordion) and an Open by default setting (whether the accordion starts expanded).
  • Liquid templates: add {% section 'disclosures' %} to templates/product.liquid where you want disclosures to appear.

Anchor to Placement in first-party themesPlacement in first-party themes

The disclosures accordion is designed to be full width, which affects where it can live:

  • Dawn: places disclosures in a standalone, full-width section as a sibling below Product information. Dawn's Product information is locked to one column of a two-column grid, so a block there can't be full width. Making disclosures its own section keeps the editor's block order matching the rendered order.
  • Horizon: places disclosures in a static block inside Product information (at the bottom). Horizon allows blocks within Product information to render outside the constrained column, so the block stays full width while editor ordering stays accurate.

Merchants can edit the heading, toggle the default open state, move the section/block, and remove it entirely if they prefer to surface disclosures elsewhere.


Anchor to Surface disclosures in checkoutSurface disclosures in checkout

Shopify Plus

Checkout UI extensions are available only to Shopify Plus merchants.

Disclosures render natively on the product details page and cart. To also surface them in checkout, use a Checkout UI extension that reads each product's disclosure metaobjects from the Storefront API (see Read disclosures with the Storefront API) and renders them.

The extension needs Storefront API access — the api_access capability in your shopify.extension.toml:

[extensions.capabilities]
api_access = true

Anchor to Access disclosures from agentic channelsAccess disclosures from agentic channels

Disclosures are exposed to agentic commerce through the Universal Commerce Protocol (UCP) lookup_catalog response, reusing the message_warning schema rather than a new top-level field.

A disclosure is a message with type: "warning" and presentation: "disclosure". The top-level messages[] array is the canonical surface, and each entry is anchored to a product via a JSONPath path. This keeps the bulk case unambiguous: a response with twenty products carrying different disclosures yields twenty messages[] entries, each pointing at its product node.

{
"messages": [
{
"type": "warning",
"code": "prop65",
"path": "$.products[0]",
"content": "**California Proposition 65 Warning.** This product can expose you to chemicals including acrylamide, which is known to the State of California to cause cancer.",
"content_type": "markdown",
"presentation": "disclosure",
"image_url": "https://{cdn}/prop65-warning.svg",
"url": "https://www.p65warnings.ca.gov"
},
{
"type": "warning",
"code": "choking_hazard",
"path": "$.products[2]",
"content": "**Choking hazard.** Small parts. Not suitable for children under 3 years.",
"content_type": "markdown",
"presentation": "disclosure",
"image_url": "https://{cdn}/choking-hazard.svg"
}
]
}

Disclosures are sourced at the product grain in Core (only active-status entries are returned). Per the UCP contract they're then surfaced to the variant grain in the Catalog response, with one messages[] entry per disclosure anchored by path (no aggregation or dedupe). The shape extends to future variant-grain disclosures via $.products[N].variants[M] without breaking.


Anchor to Jurisdictions and display preferencesJurisdictions and display preferences

  • Jurisdictions use country and subdivision codes (for example, US, US-CA). Regional groupings (EU, NAFTA) are not modeled. For taxonomized disclosures jurisdiction comes from the taxonomy leaf; for custom disclosures, set jurisdictions on the entry.
  • Display preferences ({"surfaces": ["product_page"]}) declare which surfaces a disclosure should render on. The surfaces array accepts product_page, cart, and checkout. Disclosures render natively on the product details page and cart; to render in checkout, use a Checkout UI extension (see Surface disclosures in checkout).

Anchor to Developer tools and resourcesDeveloper tools and resources


Was this page helpful?