> ## 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.

### 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)
