Importing Metafields
The Metafields import writes custom metafield values into your Shopify store from a CSV/Excel file, a Google Sheet, or directly from another connected store. It is a dedicated metafields sheet — one metafield per row — so a single file can set values on many different kinds of records at once: products, variants, collections, customers, orders, pages, companies, company locations, metaobjects, articles, and the shop itself.
Under the hood every row is applied with Shopify's metafieldsSet mutation.
That mutation is an upsert keyed on owner + namespace + key, so running the
same file twice updates the same metafields instead of creating duplicates,
and a row with a blank value deletes the metafield.
For the wizard itself — uploading, mapping, dry runs, schedules — see Importing into EcomSolo.
Metafield values vs. definitions
This import writes metafield values — the data stored on each record. It does not create or manage metafield definitions (the schema under Settings → Custom data that gives a metafield its name, type and validation). This mirrors how Matrixify works: values are imported, definitions are set up separately.
You can import values with or without a matching definition:
- No definition: the value is still stored on the record (Shopify accepts it through the API), and it appears in EcomSolo's Metafields dashboard and via the Shopify API — but it will not show in the product/customer editor in Shopify Admin, which only renders metafields that have a definition.
- A definition exists: the row's Type must match the definition's type, and the value must pass the definition's validation, or the row fails.
So for a value to be both stored and visible/editable in Shopify Admin, create the definition first in Shopify Admin → Settings → Custom data, then import the values against it.
Importing or exporting the definitions themselves is not supported yet — and neither Matrixify does it. Create your definitions in Shopify Admin. (Definition import/export is planned as a separate feature.)
Columns
| Column | Required | Notes |
|---|---|---|
| Owner | yes | The kind of record this metafield attaches to. One of product, variant, collection, customer, order, page, company, companylocation, metaobject, article, shop. (For shop there is only one owner, so no id/handle is needed.) |
| Owner ID | one of ID / Handle | The owner's Shopify ID — either a full GID (gid://shopify/Product/123) or a bare numeric id (123). |
| Owner Handle | one of ID / Handle | The owner's natural key instead of an id (see the table below). Use whichever you have; Owner ID wins if both are present. |
| Namespace | yes | The metafield namespace, e.g. custom or my_fields. |
| Key | yes | The metafield key within the namespace, e.g. care_instructions. |
| Type | yes | The Shopify metafield type — must match the definition (see Types). |
| Value | see note | The value, written according to its type. Leave blank to delete the metafield. |
Common export headers map automatically: Owner Type / Object → Owner,
ID → Owner ID, Handle → Owner Handle, Metafield Namespace → Namespace,
Metafield Key → Key, Metafield Type → Type, Metafield Value → Value.
Owner Handle conventions
When you use Owner Handle instead of an id, the natural key depends on the owner type:
| Owner | Owner Handle is… | Example |
|---|---|---|
product | the product handle | blue-cotton-tee |
variant | product_handle.variant_title | blue-cotton-tee.Large / Blue |
collection | the collection handle | summer-sale |
customer | the customer email | sam@example.com |
order | the order name (with #) | #1234 |
page | the page handle | about-us |
company | the company name | Acme Wholesale |
companylocation | the company location name | Acme — Berlin |
article | blog_handle.article_handle | news.spring-launch |
metaobject | definition_handle.entry_handle | authors.jane-doe |
shop | (none — leave blank) |
Types
The Type must be a valid Shopify metafield type and must match the definition. Values are written to match the type; the grammar per family is below.
Text
single_line_text_field,multi_line_text_field— plain text. Write the text as-is.
Numbers
number_integer— a whole number, e.g.42.number_decimal— a decimal, e.g.19.99.
Boolean
boolean—TRUE/FALSE,yes/no, or1/0(case-insensitive).
Dates, colors, URLs
color— a hex color, e.g.#1a2b3c.url— an absolute URL.date—YYYY-MM-DD, e.g.2026-07-09.date_time— ISO 8601, e.g.2026-07-09T14:30:00Z.
JSON
json— a valid JSON string, e.g.{"tier":"gold","points":120}.
Measurements and money
Each of these accepts a human-readable string or the equivalent JSON object:
money—"15.00 EUR"or{"amount":"15.00","currency_code":"EUR"}.weight—"2.5 kg"or{"value":2.5,"unit":"kg"}(units:kg,g,oz,lb).volume—"1.5 l"or{"value":1.5,"unit":"l"}(units:ml,cl,l,m3, …).dimension—"30 cm"or{"value":30,"unit":"cm"}(units:mm,cm,m,in,ft,yd).
The shorthand form ("2.5 kg") passes its unit token straight through; Shopify
accepts these lowercase codes and normalises them on save (e.g. kg is stored
and returned as KILOGRAMS).
Rating
rating— JSON{"value":4.5,"scale_min":1,"scale_max":5}. The scale comes from the definition; the value must sit within it.
Link
link— a bare URL, or JSON{"text":"Read more","url":"https://…"}.
Rich text
rich_text_field— plain text is wrapped as a single paragraph. If you already have Shopify's rich-text JSON, it passes through unchanged. Limitation: HTML and Markdown are not deep-converted into rich-text structure — headings, lists, links and bold/italic markup are treated as literal text. For structured rich text, supply Shopify's rich-text JSON.
References
The reference types point at another record. Give the target's natural key
(the same conventions as Owner Handle) or a full
gid://…:
| Type | Value is the target's… |
|---|---|
product_reference | product handle or gid |
variant_reference | product_handle.variant_title or gid |
collection_reference | collection handle or gid |
customer_reference | customer email or gid |
order_reference | order name (#1234) or gid |
page_reference | page handle or gid |
company_reference | company name or gid |
metaobject_reference | definition_handle.entry_handle or gid |
article_reference | blog_handle.article_handle or gid |
file_reference | the file's handle/URL or gid |
Lists
Any type can be a list by prefixing list. — e.g.
list.single_line_text_field, list.number_integer,
list.product_reference. Separate the values with a comma, semicolon, or
newline, or supply a JSON array:
Blue;Green;Red
["Blue","Green","Red"]
List reference types work the same way — a list of handles/emails/names (or gids), separated or as a JSON array.
How metafields are matched
Every row is matched on owner + namespace + key:
- If that metafield already exists on the owner, its value is updated.
- If it doesn't exist, it is created.
- If the Value is blank, the metafield is deleted (matching the behaviour of Matrixify-style sheets).
Because metafieldsSet is an upsert, the whole import is idempotent —
re-running the same file produces the same end state.
Operations
Step 1 of the wizard picks what the file does:
- Create — new metafields only; a row whose metafield already exists on the owner fails.
- Update — existing metafields only; a row that doesn't yet exist fails.
- Upsert (the default) — creates or updates as needed, and a blank Value deletes.
- Delete — permission-gated; rows carry the identity columns only (Owner + Namespace + Key) and the metafield is removed. A metafield that isn't there is skipped, so re-running the same delete file is safe.
Deleting a metafield removes that one value from the record; it never touches the metafield definition. There's no undo, but you can re-import the value at any time.
Large sheets import fast. Rows are dispatched to Shopify in batches (up to 25 metafields per call), so a sheet with thousands of rows imports far quicker than one row at a time — with no change to the columns or behaviour above.
Importing from another store
On the wizard's Source step, pick From another store to copy metafields from a connected store — no file needed. Owner, namespace, key, type and value come across, matched by owner + namespace + key. Combined with a schedule this becomes a recurring one-way metafield sync.
Results and freshness
Metafields you create, update or delete take effect in Shopify immediately, and EcomSolo's dashboards refresh within seconds of the run.
Sample file
A ready-to-run sample lives at
features/importing/examples/metafields-sample.csv — text, number, boolean,
JSON, money, weight, date, URL, a list, references, an owner-by-GID row, a
blank-value delete row, and both product and customer owners:
Owner,Owner ID,Owner Handle,Namespace,Key,Type,Value
product,,blue-cotton-tee,custom,care_instructions,single_line_text_field,Machine wash cold
product,,blue-cotton-tee,custom,warranty_years,number_integer,2
product,,blue-cotton-tee,custom,is_bestseller,boolean,TRUE
customer,,sam@example.com,loyalty,profile,json,"{""tier"":""gold"",""points"":120}"
order,,#1234,custom,gift_wrap_fee,money,5.00 EUR
Dry run shows the create/update/delete split before anything is written.
Troubleshooting
| Error in the report | What it means / what to do |
|---|---|
row requires an Owner | Every row needs an owner type. |
could not resolve owner | The Owner ID / Owner Handle didn't match a record — check the handle convention for that owner type. |
type does not match the metafield definition | A definition already exists for that owner + namespace + key with a different type. Match the Type column to the definition (or change the definition) in Settings → Custom data, then re-run. |
| Value imported but not visible in Shopify Admin | The value saved fine, but there's no definition, so Shopify Admin's product/customer editor doesn't render it. Create a definition in Settings → Custom data (or view it in EcomSolo's Metafields dashboard / the API). |
invalid value for type <type> | The value doesn't parse for that type (bad JSON, wrong date format, unit not recognised, etc.). |
FAQ
Will re-importing create duplicates? No — rows match on owner + namespace +
key and metafieldsSet upserts, so re-runs update in place.
How do I delete a metafield? Leave the Value blank on its row. The metafield is removed.
Do I have to create definitions first? No — values import with or without a definition. But without one the metafield won't appear in Shopify Admin's editor (it's still there via the API and EcomSolo's dashboard), and there's no type validation. Create the definition in Settings → Custom data for full visibility and validation. This import never creates definitions — same as Matrixify.
Can I import the metafield definitions themselves? Not yet — this import handles values only (as does Matrixify). Create definitions in Shopify Admin; definition import/export is planned separately.
Can one file target different owners? Yes — that's the point of the dedicated metafields sheet. Mix products, customers, orders and more in the same file; the Owner column on each row decides where it lands.