Shopify export → Hyperzod import — deterministic, no LLM

Convert Shopify Product CSV to Hyperzod

Drop your Shopify product export and get a Hyperzod-ready import CSV — variant rows collapsed into option groups, Variant Price recalculated as deltas over the cheapest variant, Body (HTML) cleaned, images reattached by Image Position. No manual restructuring, no invented columns — only real Shopify fields and real CSVMeta headers.

Detects Shopify format automatically · Holds file until you say convert · Validates before export

1 row →

per product after grouping by Handle

Delta

base = MIN Variant Price, variants = price − base

Quote-aware

Body (HTML) with commas & quotes parsed safely

Shopify and Hyperzod describe the same product completely differently

Shopify exports one row per variant — every size and color combination gets its own row with its own Variant Price, Variant SKU, Image Src and Option1 Value. Product fields like Title, Body (HTML), Vendor appear only on the first row of each Handle. Hyperzod expects one row per product with grouped OPTION*.VARIANTS cells and additive pricing. Converting by hand means collapsing dozens of rows, cleaning HTML, and hand-calculating every delta — and a single misplaced quote shifts columns for the whole file.

Rows must be collapsed by Handle

A product with 2 sizes × 3 colors = 6 rows plus 5 image rows. Blank Handle on continuation rows must still group correctly, and Title lives only on row 1.

Prices are not copy-paste

Hyperzod stores the cheapest variant as the base PRODUCT.PRICE.SELLING and each other price as a delta. Pasting the full Shopify price into the variant field breaks every future base-price change.

Body HTML and quoting break easily

Body (HTML) contains commas, quotes and newlines. Without quote-aware parsing the row splits mid-description and every column after it shifts — image URLs land in the wrong field.

CSV Pilot does the restructuring in code — group by handle, build option trees, recalculate deltas, strip HTML, reattach images — so the exported file already matches the importer.

How it works

Upload, confirm, review, export — the hold-and-convert flow never mutates your file until you ask.

01

Upload the Shopify export

Click Upload CSV on /tool and select your Shopify product CSV. Detection is case-insensitive and matches Handle/URL handle + Variant Price/Price/SKU/Option1. The file is held unchanged — nothing is converted yet.

02

Ask to convert

Tell the agent: “Convert this CSV for Hyperzod”. The code groups rows by Handle (blank handles = continuation), drops Default Title placeholders, and builds the option tree from Option1–3 Name/Value.

03

Code rebuilds the product

For each Handle: base = MIN Variant Price, variant deltas = price − base (rounded 2dp), HTML stripped from Body (HTML), Tags split on commas, Status = active only if status active/true and Published ≠ FALSE, images sorted by Image Position.

04

Review & export

Check the preview grid — base price, option groups, inventory, images. Fix any cell inline. Then Export CSV. The download uses real CSVMeta headers and is ready for Hyperzod Admin → Catalogue → Import.

Supported data — only real Shopify fields and real Hyperzod columns

No invented headers. Left table: every Shopify column the converters in lib/shopify-convert.ts actually read (with aliases). Right table: every Hyperzod header from lib/schema.ts → CSVMeta. Fields you don't provide stay empty — nothing is hallucinated.

Shopify source columns → mapping

Shopify columnHyperzod result
Handle / URL handleGrouping key only — not copied to PRODUCT.ID
TitlePRODUCT.NAME (required)
Body (HTML) / Description / BodyPRODUCT.DESCRIPTION — HTML stripped
VendorCategory fallback (3rd) if Type/Category empty
Product CategoryPRODUCT.CATEGORY fallback (2nd) unless "uncategorized"
TypePRODUCT.CATEGORY — first choice (most specific)
TagsPRODUCT.TAGS — split on commas
Published / Published on online storePRODUCT.STATUS — active only if Published ≠ FALSE and Status active
Statusactive / draft / archived → PRODUCT.STATUS
Option1 Name / Value … Option3OPTION1/2/3.NAME + VARIANTS (up to 3 levels)
Variant Price / PricePRODUCT.PRICE.SELLING = MIN price; others = deltas
Variant Compare At Price / Compare-at pricePRODUCT.PRICE.COMPARE = MAX compare (or 0)
Variant SKU / SKUPRODUCT.SKU (first non-empty) + per-variant sku
Variant Inventory Qty / Inventory quantityinventory per variant; blank → undefined (no stepper)
custom Quantity (metafields.custom)Alternate inventory source when the above empty
Image Src / Product image URLPRODUCT.IMAGES — first sorted image
Variant Image / Variant image URLVariant imageUrl
Image Position / Image positionSort key for images (defaults to 1)
Cost per itemPRODUCT.PRICE.COST + variant costPrice
Variant Barcode / BarcodeDropped — warning per product (no Hyperzod field)
Gift CardRow skipped entirely when true/1/yes

Quote-aware parsing via parseCsvRows keeps Body (HTML) commas inside the field.

Hyperzod destination (CSVMeta)

CSV headerField
PRODUCT.NAMEname — required
PRODUCT.DESCRIPTIONdescription — HTML-cleaned Body
PRODUCT.SKUsku — deduped across products
PRODUCT.CATEGORYcategory — Type / Category / Vendor → General
PRODUCT.PRICE.SELLINGsellingPrice — MIN Variant Price
PRODUCT.PRICE.COSTcostPrice — Cost per item
PRODUCT.PRICE.COMPAREpriceCompare — MAX compare-at (or 0)
PRODUCT.MIN.MAX.QUANTITYminQty / maxQty — empty if not in Shopify
PRODUCT.TAX_PERCENTtaxPercent — empty if not in Shopify
PRODUCT.STATUSstatus — active / inactive
PRODUCT.INVENTORYinventory — sum of variant inventories
PRODUCT.LABELSlabels — empty (no Shopify source)
PRODUCT.TAGStags — from Tags
PRODUCT.IMAGESimageUrl — first sorted image
PRODUCT.IDid — not set from Handle (fresh import)
Option CSV headerField & values
OPTION*.NAMEoption name — e.g. Size
OPTION*.TYPEsingle | multiple
OPTION*.ENABLE_RANGEtrue / false
OPTION*.RANGE[min, max]
OPTION*.REQUIREDtrue / false
OPTION*.VIEWlist | card
OPTION*.VARIANTSname, price delta, cost, inventory, description, image

VARIANTS encoding

Each variant is name, price, cost, inventory, description, image. Price is the delta over base — e.g. M,10.00,0.00,50,,. Blank inventory keeps the field empty so Hyperzod does not enable the per-variant stepper.

Transformation example — real headers, real delta math

3 Shopify variant rows for one Handle → 1 Hyperzod product row with deltas that sum to the exact same prices.

Shopify CSV — 3 rows, 1 product
HandleTitleBody (HTML)Option1 NameOption1 ValueVariant PriceVariant Compare At PriceVariant SKUVariant Inventory QtyImage SrcImage Position
classic-teeClassic T-Shirt<p>Soft cotton tee</p>SizeS19.9924.99TEE-S50https://cdn.example/tee.jpg1
classic-teeSizeM29.9924.99TEE-M30https://cdn.example/tee.jpg1
classic-teeSizeL39.9924.99TEE-Lhttps://cdn.example/tee-2.jpg2
Before: Shopify row math

Handle=classic-tee appears on all 3 rows. Only row 1 has Title + Body (HTML). Each row carries its own Variant Price and Option1 Value.

  • • Customer pays S = $19.99, M = $29.99, L = $39.99 — flat totals per row
  • • Image rows would duplicate combos with new Image Src — deduped by value tuple
  • • Blank inventory on L stays blank, not 0
After: Hyperzod row (one of N)
PRODUCT.NAMEClassic T-Shirt
PRODUCT.DESCRIPTIONSoft cotton tee
PRODUCT.CATEGORYT-Shirts (from Type)
PRODUCT.PRICE.SELLING19.99 ← MIN(19.99,29.99,39.99)
PRODUCT.PRICE.COMPARE24.99 ← MAX compare-at
PRODUCT.STATUSactive
PRODUCT.IMAGEShttps://cdn.example/tee.jpg
OPTION1.NAME / TYPE / REQUIRED / VIEWSize / single / true / list
OPTION1.VARIANTSS,0.00,…; M,10.00,…; L,20.00,…
Deltas: S +$0.00 → $19.99, M +$10.00 → $29.99, L +$20.00 → $39.99. Change the base once and every size reprices.

Delta verification

base 19.99 + S 0.00 = 19.99 ✓  |  base 19.99 + M 10.00 = 29.99 ✓  |  base 19.99 + L 20.00 = 39.99 ✓

L's blank inventory exports as empty — Hyperzod leaves per-variant stepper disabled. For multi-level products (Size → Color) interior deltas are MIN of child prices and leaf deltas carry the remainder, so any path still sums to the exact Shopify price.

"S,0.00,0.00,50,Soft cotton tee,https://cdn.example/tee.jpg;M,10.00,0.00,30,,;L,20.00,0.00,,,https://cdn.example/tee-2.jpg"

Example workflow — Classic T-Shirt with Size and Color

A real Shopify export shape, end-to-end. Copy this flow for your own catalog.

Upload and confirm

Your Shopify CSV

Handle=classic-tee · Title=Classic T-Shirt · Body (HTML)=<p>Soft cotton tee</p> · Vendor=Acme · Tags=vintage, summer · Published=TRUE · Status=active · Option1=Size (S/M/L) · Option2=Color (Black/White) · Variant Price per combo · Image Src×2 positions

Detection: Shopify CSV — held unchanged ✓

Tell agent: “Convert this CSV for Hyperzod”

Grouping key classic-tee collapses 6 variant rows + 2 image rows into one product.

Code conversion
  • Vendor + Type + Tags preserved; Type wins category
  • Body (HTML) → "Soft cotton tee" (tags stripped)
  • Option1 Size (S/M/L) + Option2 Color (Black/White) nest — S cheapest → base
  • Variant Price 19.99–39.99 → base 19.99, interior deltas = MIN child, leaf deltas remainder
  • Images sorted by Position; first becomes PRODUCT.IMAGES
  • Barcode rows counted → warning, not column; Gift Card rows skipped
Tip: If your export has blank Handle on later rows, it still groups — lastHandle carry-over handles import-template style.
Review and export

Preview grid

Classic T-Shirt → PRODUCT.PRICE.SELLING 19.99, Size group with Color nested

Check: cheapest combo is base? Inventory sums? Image resolves? SKU deduped?

Fix inline or chat follow-up

“Make L +$2 cheaper” → recalculates leaf delta only

Then Export CSV — the file matches the preview exactly and imports via Admin.

Open the tool

Shopify-aware — 8 things it gets right that generic CSV tools don't

Handle grouping + blank continuations

Groups every row by Handle/URL handle. Blank handle inherits the last handle — so import-template exports where only row 1 has the handle still collapse correctly.

Default Title dropped

Any Option level where every value is “Default Title” or empty is removed. Simple products stay flat with no fake Title option leaking into Hyperzod.

Body (HTML) cleaned

Strips tags, converts <br> to newlines, decodes &amp; &nbsp; etc., and quotes the result — so a description with commas never shifts the VARIANTS image column.

Category precedence is correct

Resolves PRODUCT.CATEGORY as Type > non-uncategorized Product Category > Vendor > General — matching how merchants actually use those fields.

True Hyperzod delta pricing

Builds the nested option tree (up to 3 levels, Option1-3), then converts every price to base + delta so the sum along any Size×Color path equals the original Variant Price.

Multi-image deduping

Dedupes variant combinations by option-value tuple (same combo repeated with different Image Src) and keeps the first non-empty sku/image. Product images sorted by Image Position.

Inventory semantics preserved

Blank inventory stays undefined (empty CSV field → no per-variant stepper). Only parsed numbers are carried; parent inventory = sum of children's inventories.

Gift cards skipped, barcodes warned, SKUs deduped

Gift Card = true/1/yes rows are skipped with giftCardsSkipped count. Barcodes contribute to a per-product warning. Final pass runs dedupeSkus so exported PRODUCT.SKU values are unique.

Limitations — honest scope

The converter is deterministic and exact within its target, but it is not a full Shopify API sync and it does not bypass Hyperzod import rules.

Barcode has no field

Variant Barcode / Barcode is counted per product and reported as a warning (“dropped — Hyperzod has no barcode field”), never as a column.

Gift cards are skipped

Any row with Gift Card = true/1/yes is excluded from the export and tallied as giftCardsSkipped. Add gift-card logic in Hyperzod separately.

Max 3 option levels

Shopify supports Option1–3 and the builder mirrors it. A fourth option column has no source and is ignored. Interior price = MIN child; three levels nest correctly.

Inventory is a snapshot

PRODUCT.INVENTORY reflects the CSV at conversion time, not live Shopify stock. No metafields except custom Quantity (product.metafields.custom.custom_quantity) are read.

No image hosting

Only http URLs from Image Src / Product image URL are attached; the tool does not upload or host images. Provide public URLs you control.

Handle never becomes PRODUCT.ID

The Shopify Handle is a grouping key only. Hyperzod PRODUCT.ID stays empty so every import is fresh — handle does not leak into the product identity.

Validation is preview, not the import itself

Currency symbols on Variant Price are stripped and commas removed, but Hyperzod's importer has the final say. Fix flagged rows in the grid and export again.

Draft / Archived maps to inactive

PRODUCT.STATUS is active only when Status is active/true and Published ≠ FALSE. Everything else becomes inactive — review draft products before publishing.

Frequently asked questions

What Shopify file should I upload — export or import template?

Either. The converter detects both the Admin export (Handle, Body (HTML), Variant Price, Option1 Name/Value, Image Src) and the import-template format (URL handle, Description, Price, SKU, Option1 name/value) via case-insensitive header lookup. One row per variant is expected — blank Handle rows are treated as continuations of the last handle.

How is the Hyperzod base price and variant delta calculated?

Hyperzod uses additive pricing. The product base PRODUCT.PRICE.SELLING is the minimum Variant Price across all combinations. Each nested variant price is stored as a delta: combination price minus the base. The cheapest variant carries 0.00 delta; others carry the difference rounded to two decimals. Summing base plus deltas along any option path reconstructs the exact Shopify Variant Price.

What happens to Default Title placeholder products?

Shopify exports simple products without real options as Option1 Name = Title, Option1 Value = Default Title. The converter drops any option level where every value is empty or Default Title, so those products remain flat with no fake option group — exactly as Hyperzod expects.

Are gift cards and barcodes carried over?

Rows where Gift Card is true (true/1/yes) are skipped entirely and counted in the report as giftCardsSkipped — Hyperzod has no gift-card product type. Barcode and Variant Barcode columns are dropped with a warning per product (e.g. "Classic T-Shirt — 2 barcodes dropped — Hyperzod has no barcode field") because there is no matching Hyperzod field.

How are images and multi-image rows handled?

Image Src and Product image URL columns are collected by Image Position, deduped by URL, sorted by position, and the first image becomes PRODUCT.IMAGES. If the export repeats the same variant combination on multiple rows with different Image Src values, the converter dedupes by the option-value tuple and keeps the first non-empty image rather than creating duplicate variants. Position defaults to 1 when missing.

How is inventory mapped — does blank mean zero?

No. Variant Inventory Qty / Inventory quantity / custom Quantity (product.metafields.custom.custom_quantity) is parsed only when the cell is non-empty. Blank or missing leaves the Hyperzod inventory undefined, which exports as an empty field so the importer does NOT enable the per-variant quantity stepper. A value of 0 is preserved as 0. Parent variant inventory is the sum of its child combinations.

What product fields are actually supported?

Only fields the code reads: Handle / URL handle, Title, Body (HTML) / Description / Body (HTML stripped), Vendor, Product Category / Product category, Type, Tags (comma-split), Published (Published on online store) plus Status (active/inactive), Option1 Name/Value, Option2 Name/Value, Option3 Name/Value, Variant Price / Price, Variant Compare At Price / Compare-at price, Variant SKU / SKU, Variant Inventory Qty, Image Src / Product image URL, Image Position, Variant Image URL, Cost per item. Everything else is ignored safely with quote-aware CSV parsing for Body HTML that contains commas, quotes and newlines.

What file do I get back and how do I import it?

You get a Hyperzod-compatible CSV with PRODUCT.* headers (PRODUCT.NAME, PRODUCT.DESCRIPTION, PRODUCT.SKU, PRODUCT.PRICE.SELLING, PRODUCT.PRICE.COST, PRODUCT.PRICE.COMPARE, PRODUCT.MIN.MAX.QUANTITY, PRODUCT.TAX_PERCENT, PRODUCT.STATUS, PRODUCT.INVENTORY, PRODUCT.LABELS, PRODUCT.CATEGORY, PRODUCT.TAGS, PRODUCT.IMAGES) plus OPTION1.NAME/TYPE/ENABLE_RANGE/RANGE/REQUIRED/VIEW/VARIANTS (and OPTION2/OPTION3 as needed) with VARIANTS cells quoted and delta-priced. Import it via Hyperzod Admin → Catalogue → Import → CSV Import. SKU duplicates are deduped automatically.

Ready to migrate your Shopify catalog?

Upload the export, say “Convert this CSV for Hyperzod”, review the deltas and export — no spreadsheet formulas, no column guessing. Only real Shopify fields to real Hyperzod columns.

Free · No signup · Same tool as homepage · Handles, deltas and images preserved