41 "summary": "Attaching an existing customization to a product by id ALONE fails. VERIFIED LIVE 2026-08-16: product.modifiers[] = [{ id: <customization GUID>, mandatory: false }] → 400 REQUIRED_ONE_OF_FIELD 'freeTextSettings or choicesSettings must not be empty', and adding freeTextSettings without a title → 400 MIN_LENGTH on modifiers[0].modifierSettings.freeTextSettings.title. A ConnectedModifier must restate name, modifierRenderType, and the full settings block (title included) next to the id. Note the field renames between the two surfaces: the standalone customization uses freeTextInput/customizationRenderType, the product-side modifier uses freeTextSettings/modifierRenderType — a transform that reuses the create payload verbatim will fail."
46 "summary": "VERIFIED LIVE 2026-08-16: a product created with modifiers[] and NO id auto-creates a fresh reusable customization and returns its server-assigned id. Convenient for a single-pass write, but it mints one customization per product — a source add-on group shared by 200 products becomes 200 near-duplicate customizations unless the group is created once up front and attached by id. Create-then-attach is the correct path for shared groups."
47 },
48 {
49 "code": "modifier-not-option",
50 "severity": "warning",
51 "summary": "Add-on plugin fields collect input without creating variants — author them as customizationType MODIFIER, never PRODUCT_OPTION (options create product variants that affect inventory, SKU, and pricing; docs read 2026-08-16). A wrong type multiplies the variant matrix and cannot be fixed in place (customizationType is immutable)."
56 "summary": "OPEN FIDELITY QUESTION: the Customization object carries no conditional-visibility mechanism (full request schema read 2026-08-16 — nothing rules-shaped). Source conditional rules (e.g. PPOM per-field conditions, per-variation _ppom_variation_meta_rules) need a field-by-field pairing at mapping time; expect reported loss unless a pairing is found."
57 },
58 {
59 "code": "per-option-pricing-pairing-needed",
60 "severity": "warning",
61 "summary": "OPEN FIDELITY QUESTION: choices and free-text inputs carry defaultAddedPrice (decimal 0–999999999.99, maxScale 3 — docs read 2026-08-16), a plausible home for per-option add-on prices, but it is a DEFAULT on the reusable customization, not a per-product override. Field-by-field pairing needed at mapping time when the source prices the same option differently per product."
62 },
63 {
64 "code": "file-upload-no-render-type",
65 "severity": "warning",
66 "summary": "OPEN FIDELITY QUESTION: customizationRenderType supports only FREE_TEXT, TEXT_CHOICES, SWATCH_CHOICES (enum read 2026-08-16). Source file-upload / image-cropper fields have no native render type — decide per field at mapping time (drop with ledger entry, or approximate); there is no customization-based carry for uploads."
67 },
68 {
69 "code": "unique-name-type-render",
70 "severity": "warning",
71 "summary": "VERIFIED LIVE 2026-10-27: a customization is unique by (name, customizationType, customizationRenderType): a second create answers 409 ALREADY_EXISTS naming those three paths. The reviewed writer createStoresCustomization reuses the existing one by a name query filtered client-side on type and render type. Query filters on name."
72 }
73 ],
74 "mappingGuidance": [
75 "source add-on field groups (e.g. PPOM field groups) -> customization definitions, one customization per field — text inputs -> FREE_TEXT with freeTextInput (title required, <=100 chars; min/maxCharCount, max 500), choice fields -> TEXT_CHOICES with choicesSettings.choices[] (name <=50 chars, <=1000 choices), color swatches -> SWATCH_CHOICES (ONE_COLOR choices).",
76 "Per-product attachment: the source's product-to-group assignment (e.g. PPOM _product_meta_id) -> the product's customization references — product.modifiers[] ConnectedModifier entries citing the created customization ids (customizationType MODIFIER), with mandatory per attachment. Create customizations first, crosswalk group id -> customization ids, then reference them on product create; inline modifier definitions on the product also auto-create reusable customization entities if a single-pass write is preferred (but see inline-modifiers-mint-a-new-customization before choosing that path).",
77 "The ConnectedModifier entry must carry { id, name, modifierRenderType, mandatory, freeTextSettings|choicesSettings } — the id alone is rejected, and the settings block needs its own title (verified live 2026-08-16, see connected-modifier-must-restate-the-settings). Keep the restated values identical to the customization's own so the two surfaces do not drift.",
78 "Customization name <=50 chars; the server-generated key is what eCommerce catalogReference.options uses — record created ids AND keys in the crosswalk so order-side mapping (ecom) can resolve them. Observed live 2026-08-16: on a plain-ASCII name the key is simply the name verbatim ('ZZZ Probe Engraving Text'), and the freeTextInput title gets its own separate key — do not assume the key is a slugified form, read it back.",
79 "Write shared add-on groups once with createStoresCustomization, then attach them on the product with connectedModifierFromCustomization(customization) (verified live 2026-10-27: restates name, modifierRenderType and freeTextSettings/choicesSettings next to the id). The product task depends on this one task-complete.",
80 "Give same-named source groups distinct names within a run (for example \"Size\" and \"Size (2)\") when their settings or choices differ: a name collision with different settings is refused as CUSTOMIZATION_NAME_CONFLICT, never reused."
104 "note": "attachment surface: product.modifiers[] ConnectedModifier — id of a MODIFIER customization, mandatory flag, key (read 2026-08-16)"
105 },
106 {
107 "type": "review-decision",
108 "path": "anonymized-live-verification",
109 "note": "commerce.product-addon-fields; resolves the stores docs-survey 2026-08-11 'revisit when an add-on plugin is profiled' note"
110 },
111 {
112 "type": "live-run",
113 "path": "anonymized-live-verification",
114 "note": "Verified live: POST /stores/v3/customizations with { customizationType: MODIFIER, customizationRenderType: FREE_TEXT, freeTextInput: { title, minCharCount, maxCharCount, defaultAddedPrice } } → 200 with server-assigned id and key. Attaching it via product.modifiers[] took three attempts — id-only and id+settings-without-title both 400'd — and succeeded with id + name + modifierRenderType + full freeTextSettings; the created product read back the same customization id and key. A control product with an inline modifier and no id also succeeded and minted a SECOND customization. Both products and both customizations deleted afterwards (DELETE /stores/v3/customizations/{id} → 200, including the inline-minted one)."
115 }
116 ],
117 "notes": "Authored 2026-08-16 from the approved mapping behavior. Promoted to verified-live 2026-08-16 by a create → attach → readback → delete probe on an anonymized verification environment; the ConnectedModifier restatement requirement and the inline-mints-a-duplicate behaviour were both discovered there. reliability is partially-reliable, not reliable: only MODIFIER/FREE_TEXT was exercised live — TEXT_CHOICES and SWATCH_CHOICES, bulk create (POST /stores/v3/customizations/bulk), and defaultAddedPrice's effect on order totals remain docs-only, and the three OPEN FIDELITY QUESTIONS below are unchanged by this probe. Pairs with ecom/order-line-item-options for the captured order-side values (commerce.order-addon-values)."