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
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
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 in the
Extensions overview for the conventions.
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 TeamInfra’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 |
Compound types are sent as JSON in the value field:
{
"namespace": "custom",
"key": "weight",
"type": "weight",
"value": { "unit": "KILOGRAMS", "value": 1.2 },
"ownerType": "product",
"ownerId": "..."
}
Scalars are sent as-is:
{
"namespace": "custom",
"key": "warranty_years",
"type": "number_integer",
"value": 5,
"ownerType": "product",
"ownerId": "..."
}
Lists are arrays:
{
"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:
{
"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:
- Backend Redis resource caches (
g:{storeId}:product:{handle} etc.)
- LaunchMyStore frontend
PageHtmlCache
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 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