> ## Documentation Index
> Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Metafields Overview

> Add typed, namespaced custom data to any storefront resource

# Metafields

Metafields let you attach typed, namespaced data to nine resource kinds —
products, variants, collections, customers, orders, pages, blogs, articles,
and the shop itself. They are strongly-typed, server-validated, stored in a
separate polymorphic table, and exposed in Aqua/Liquid via the standard
`{owner}.metafields.{namespace}.{key}` access pattern.

Use them to:

* attach **app-specific data** to a customer, order, or product (e.g.
  loyalty tier, subscription status, AI-generated badge text)
* expose **structured fields** to merchants under *Settings → Custom data*
  that they can fill in per resource and reference in their theme
* store **references** between resources (`product_reference`,
  `collection_reference`, `file_reference`) so themes can link related items

<Note>
  **Apps store all per-store runtime data here.** The platform does not give
  apps a filesystem to write to — install config, feature toggles, per-resource
  counters, event logs, etc. all live as metafields under an
  `app_<handle_with_underscores>` namespace. See
  [Where to store app data](/extensions/overview#where-to-store-app-data) in the
  Extensions overview for the conventions.
</Note>

## Two API contexts

Metafields are exposed through two parallel REST surfaces with different
auth and capabilities.

| Context | Base path | Auth | Use case |
| - | - | - | - |
| **App API** (this doc) | `/api/v1/...` | OAuth scopes `read_metafields` / `write_metafields` | Apps reading/writing their own metafields |
| **Admin API** | `/metafields...`, `/metafield-definitions...` | Merchant JWT | Admin UI, bulk save, definition management |

Apps see and edit only the metafields they wrote (scoped by `appId`). The
Admin API is what powers the admin's *Settings → Custom data* and the
inline editor on resource detail pages — apps cannot use it.

| Operation | App API (OAuth) | Admin API (Merchant JWT) |
| - | - | - |
| List metafields | ✓ `GET /api/v1/metafields.json` | ✓ `GET /metafields` |
| Read by id | – | ✓ `GET /metafields/:id` |
| Upsert one | ✓ `POST /api/v1/metafields.json` | ✓ `POST /metafields` |
| Bulk upsert (N at once) | – | ✓ `POST /metafields/bulk` |
| Update by id | ✓ `PUT /api/v1/metafields/:id.json` | ✓ `PUT /metafields/:id` |
| Delete by id | ✓ `DELETE /api/v1/metafields/:id.json` | ✓ `DELETE /metafields/:id` |
| Definitions CRUD | – | ✓ `/metafield-definitions/...` |

## Owner types (nine)

The `ownerType` field on every metafield identifies the kind of resource it
attaches to. **Validation is case-sensitive lowercase** — sending `"PRODUCT"`
returns a validation error.

| `ownerType` | Resource | Aqua/Liquid drop |
| - | - | - |
| `shop` | Account / store | `shop.metafields` |
| `product` | Product | `product.metafields` |
| `variant` | Product variant | `variant.metafields` |
| `collection` | Category | `collection.metafields` |
| `customer` | Client | `customer.metafields` |
| `order` | Order | `order.metafields` |
| `page` | Page | `page.metafields` |
| `blog` | Blog | `blog.metafields` |
| `article` | Article (blog post) | `article.metafields` |

## Supported types (22)

Each metafield carries a `type` that determines validation, decoding, and
how it renders in Aqua/Liquid.

<Note>
  The full 22-type catalogue below — including `list.*` and all six reference
  types — is the **Admin / merchant API**. The **OAuth App API**
  (`/api/v1/...`) accepts only a **21-value subset**: no `list.*` variants,
  and references are limited to `product_reference` and `file_reference`
  (not `collection_reference`, `variant_reference`, `customer_reference`, or
  `page_reference`). An App API write with an unsupported `type` is rejected
  with **HTTP 422**. See [Create / Upsert
  Metafield](/api-reference/metafields/create#body-parameters) for the exact
  App API enum.
</Note>

### Text

| Type | Validations | `{{ mf }}` renders |
| - | - | - |
| `single_line_text_field` | `min`, `max`, `regex`, `choices` | string |
| `multi_line_text_field` | `min`, `max`, `regex` | string |
| `rich_text_field` (alias `rich_text`) | – | raw HTML, **unescaped** |

### Numeric / Boolean / Color

| Type | Validations | Renders |
| - | - | - |
| `number_integer` (alias `integer`) | `min`, `max` | integer toString |
| `number_decimal` | `min`, `max`, `max_precision` | decimal toString |
| `boolean` | accepts `true`/`false`/`"true"`/`"false"`/`0`/`1` | `"true"` / `"false"` |
| `color` | 6-char hex `#RRGGBB` only | `"#RRGGBB"` |

### Date / Time

| Type | Format | Validations |
| - | - | - |
| `date` | ISO 8601 `YYYY-MM-DD` | `min`, `max` (date strings) |
| `date_time` | ISO 8601 with optional time + timezone | `min`, `max` |

### Measurements

Stored as `{ unit, value }` objects. Renders as `"<value> <unit>"`.

| Type | Allowed `unit` values |
| - | - |
| `weight` | `KILOGRAMS`, `GRAMS`, `POUNDS`, `OUNCES` |
| `dimension` | `METERS`, `CENTIMETERS`, `MILLIMETERS`, `INCHES`, `FEET`, `YARDS` |
| `volume` | `MILLILITERS`, `CENTILITERS`, `LITERS`, `CUBIC_METERS`, `FLUID_OUNCES`, `PINTS`, `QUARTS`, `GALLONS` (+ `IMPERIAL_*` variants) |

### Money / Rating

| Type | Shape | Notes |
| - | - | - |
| `money` | `{ amount: "12.50", currency_code: "USD" }` | currency must be ISO-4217. Pipe through `\| money` in Liquid. |
| `rating` | `{ scale_min, scale_max, value }` | `scale_max > scale_min`, `scale_min ≤ value ≤ scale_max` |

### URL / JSON

| Type | Notes |
| - | - |
| `url` | parsed via `new URL()`. Default schemes `https`, `http`, `mailto`, `tel`. Override with `validations.allowed_schemes`. |
| `json` (alias `json_string`) | any JSON-serialisable value |

### References

Stored as id strings; the backend optionally verifies the id exists at write
time.

`product_reference`, `variant_reference`, `collection_reference`,
`customer_reference`, `page_reference`, `file_reference`

### Lists

`list.<innerType>` — array of items of `innerType`. Inner-type validations
apply to each item.

| Validation | Effect |
| - | - |
| `list_min` / `list_max` | array length bounds |
| inner-type rules | applied to each item |

## Wire format

Compound types are sent as JSON in the `value` field:

```json theme={null}
{
  "namespace": "custom",
  "key": "weight",
  "type": "weight",
  "value": { "unit": "KILOGRAMS", "value": 1.2 },
  "ownerType": "product",
  "ownerId": "..."
}
```

Scalars are sent as-is:

```json theme={null}
{
  "namespace": "custom",
  "key": "warranty_years",
  "type": "number_integer",
  "value": 5,
  "ownerType": "product",
  "ownerId": "..."
}
```

Lists are arrays:

```json theme={null}
{
  "namespace": "custom",
  "key": "tags",
  "type": "list.single_line_text_field",
  "value": ["red", "limited", "sale"],
  "ownerType": "product",
  "ownerId": "..."
}
```

## Validation errors

Type or validation failures return HTTP 400 with a structured `errors` array:

```json theme={null}
{
  "statusCode": 400,
  "message": "Metafield value validation failed",
  "errors": [
    { "field": "value", "message": "value must be <= 25" }
  ]
}
```

## Cache invalidation

Writes to a metafield automatically expire the relevant storefront
caches — both the cached copy of the owning resource and any cached page
HTML that rendered it.

The next storefront render will see the new value. **You don't need to
poll** — if you need to react to changes, register a `metafields/update`
[webhook](/api-reference/webhooks/overview) instead.

## Best practices for apps

* **Use your own namespace.** Don't write under `custom` (the merchant
  namespace). Use your app handle (e.g. `subscriptions_pro`,
  `loyalty_engine`) so merchants can see what's yours and apps don't
  collide on key names.
* **Pre-create definitions** during app install. Apps can `POST
  /metafield-definitions` (with merchant scope, not OAuth) on first install
  if you want the schema to appear in the merchant admin. For OAuth-only
  apps, write values directly without defining first.
* **Pin definitions** that merchants need to fill in — they show up on the
  resource detail page in admin.
* **Don't poll.** Use webhooks.

## See also

* [List Metafields](/api-reference/metafields/list)
* [Create / Upsert Metafield](/api-reference/metafields/create)
* [Update Metafield](/api-reference/metafields/update)
* [Delete Metafield](/api-reference/metafields/delete)
* [Bulk Upsert (Admin only)](/api-reference/metafields/bulk)
* [Definitions CRUD (Admin only)](/api-reference/metafields/definitions/list)
* [Aqua / Liquid access](/aqua/metafields)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.