Shopify Product XML (GraphQL)
The XML format documented here is used for the import of products and variants into Shopify, via the Import Products task. Each record is sent to Shopify using the productSet mutation, which creates or updates the product and its variants in a single call.
Products are matched using the first of the following that is provided: Id, the metafield named in the 'Product Identifier Metafield' task setting, then Handle. If none of these are provided, a new product is created. We recommend always providing a Handle (or the identifier metafield) so that re-imports update the existing product.
The ZynkExternalId field can be populated with the unique ID of the product from the external system. On this task it is only used by the 'Prevent Reprocessing' setting, which is disabled by default. It is not used to match existing products, because the match is decided before the Zynk lookup takes place.
Warning
When updating an existing product, any variants not provided in the XML will be removed from Shopify.
XML
Any fields not documented below are ignored by the task. Examples of where in the XML the fields should appear are shown in the samples below.
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<ZynkExternalId>BF0001</ZynkExternalId>
<Handle>bird-feeder</Handle>
<Title>Bird Feeder</Title>
<DescriptionHtml><p>A wooden bird feeder</p></DescriptionHtml>
<Vendor>Zynk</Vendor>
<ProductType>Garden</ProductType>
<Status>ACTIVE</Status>
<Tags>
<Tag>garden</Tag>
</Tags>
<Seo>
<Title>Bird Feeder</Title>
<Description>A wooden bird feeder</Description>
</Seo>
<Category>
<FullName>Animals & Pet Supplies > Pet Supplies > Bird Supplies</FullName>
</Category>
<Collections>
<Record>
<Title>Summer 2025</Title>
</Record>
</Collections>
<Metafields>
<Record>
<Namespace>custom</Namespace>
<Key>product_code</Key>
<Type>id</Type>
<Value>BF0001</Value>
</Record>
</Metafields>
<Media>
<Record>
<OriginalSource>https://example.com/images/bird-feeder.jpg</OriginalSource>
<MediaContentType>IMAGE</MediaContentType>
<Alt>Bird feeder</Alt>
</Record>
</Media>
<Options>
<Option>
<Name>Colour</Name>
<Position>1</Position>
<Values>
<Value>Red</Value>
<Value>Blue</Value>
</Values>
</Option>
</Options>
<Variants>
<Record>
<Sku>BF0001-RED</Sku>
<Barcode>5012345678900</Barcode>
<Price>8.00</Price>
<CompareAtPrice>10.00</CompareAtPrice>
<Taxable>true</Taxable>
<InventoryPolicy>DENY</InventoryPolicy>
<Position>1</Position>
<OptionValues>
<OptionValue>
<OptionName>Colour</OptionName>
<Name>Red</Name>
</OptionValue>
</OptionValues>
<InventoryItem>
<Tracked>true</Tracked>
<RequiresShipping>true</RequiresShipping>
<UnitCost>
<Amount>4.00</Amount>
</UnitCost>
<CountryCodeOfOrigin>GB</CountryCodeOfOrigin>
<HarmonizedSystemCode>442190</HarmonizedSystemCode>
<Measurement>
<Weight>
<Unit>KILOGRAMS</Unit>
<Value>0.25</Value>
</Weight>
</Measurement>
</InventoryItem>
<InventoryQuantities>
<InventoryQuantity>
<LocationName>Newcastle</LocationName>
<Name>available</Name>
<Quantity>6</Quantity>
</InventoryQuantity>
</InventoryQuantities>
</Record>
<Record>
<Sku>BF0001-BLUE</Sku>
<Price>8.00</Price>
<Position>2</Position>
<OptionValues>
<OptionValue>
<OptionName>Colour</OptionName>
<Name>Blue</Name>
</OptionValue>
</OptionValues>
</Record>
</Variants>
</Product>
</Products>
In each of the following sections most of the XML has been omitted to make the samples easier to read. The whole structure of the Products schema is used above as a reference of where fields should be in the object model.
Product
The following information can be provided at the product level.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| - | ZynkExternalId | string | Optional | Stored in Zynk's internal DB and used in conjunction with the 'Prevent Reprocessing' setting |
| identifier.id | Id | string | Optional | GraphQL ID of an existing product, e.g. gid://shopify/Product/123. Takes precedence over all other identifiers |
| handle | Handle | string | Optional | Used to match an existing product if Id and the identifier metafield are not provided. Also sets the handle on a new product |
| title | Title | string | Dependant | Required when creating a new product |
| descriptionHtml | DescriptionHtml | string | Optional | HTML must be escaped or wrapped in a CDATA section |
| vendor | Vendor | string | Optional | |
| productType | ProductType | string | Optional | |
| status | Status | enum | Optional | ACTIVE, ARCHIVED, DRAFT or UNLISTED |
| tags | Tags/Tag | string | Optional | One Tag element per tag |
| seo.title | Seo/Title | string | Optional | |
| seo.description | Seo/Description | string | Optional | |
| templateSuffix | TemplateSuffix | string | Optional | |
| giftCard | IsGiftCard | bool | Optional | |
| giftCardTemplateSuffix | GiftCardTemplateSuffix | string | Optional | |
| requiresSellingPlan | RequiresSellingPlan | bool | Optional | |
| redirectNewHandle | RedirectNewHandle | bool | Optional | Whether Shopify creates a URL redirect when the handle changes |
| combinedListingRole | CombinedListingRole | enum | Optional | PARENT or CHILD |
| claimOwnership.bundles | ClaimOwnership/Bundles | bool | Optional |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<ZynkExternalId>BF0001</ZynkExternalId>
<Handle>bird-feeder</Handle>
<Title>Bird Feeder</Title>
<DescriptionHtml><![CDATA[<p>A wooden bird feeder</p>]]></DescriptionHtml>
<Vendor>Zynk</Vendor>
<ProductType>Garden</ProductType>
<Status>ACTIVE</Status>
<Tags>
<Tag>garden</Tag>
<Tag>outdoor</Tag>
</Tags>
<Seo>
<Title>Bird Feeder</Title>
<Description>A wooden bird feeder</Description>
</Seo>
</Product>
</Products>
Category
The product's category from Shopify's standard product taxonomy. If Id is not provided, the category is looked up by FullName, or by Name if FullName is also not provided (case-insensitive). The record fails if no matching category is found.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| category | Category/Id | string | Dependant | Required if FullName and Name are not provided, e.g. gid://shopify/TaxonomyCategory/ap-2-1 |
| - | Category/FullName | string | Dependant | Required if Id and Name are not provided, e.g. Animals & Pet Supplies > Pet Supplies > Bird Supplies |
| - | Category/Name | string | Dependant | Required if Id and FullName are not provided, e.g. Bird Supplies. Use FullName where the name is not unique |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Category>
<FullName>Animals & Pet Supplies > Pet Supplies > Bird Supplies</FullName>
</Category>
</Product>
</Products>
Collections
The collections the product should belong to. Each collection must have an Id or a Title; titles are looked up in Shopify and the record fails if no collection, or more than one collection, matches.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| collections | Id | string | Dependant | Required if Title is not provided, e.g. gid://shopify/Collection/123 |
| - | Title | string | Dependant | Required if Id is not provided |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Collections>
<Record>
<Id>gid://shopify/Collection/94099996761</Id>
</Record>
<Record>
<Title>Summer 2025</Title>
</Record>
</Collections>
</Product>
</Products>
Metafields
Metafields can be provided on the product and on each variant, using the same structure.
If the 'Product Identifier Metafield' task setting is used, the product metafield with the matching Namespace and Key is used to find the existing product (when no Id is provided). That metafield must have the type id in Shopify. If the setting contains only a key, the metafield must be provided without a Namespace to be used as the identifier.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| id | Id | string | Optional | GraphQL ID of an existing metafield |
| namespace | Namespace | string | Optional | Shopify defaults the namespace if omitted |
| key | Key | string | Required | |
| type | Type | string | Dependant | Required unless the metafield has a definition in Shopify, e.g. single_line_text_field |
| value | Value | string | Required |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Metafields>
<Record>
<Namespace>custom</Namespace>
<Key>product_code</Key>
<Type>id</Type>
<Value>BF0001</Value>
</Record>
</Metafields>
</Product>
</Products>
Media
Media files for the product. Use Media, not Files; the Files element is ignored by the task.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| files.id | Id | string | Dependant | GraphQL ID of an existing file. Required if OriginalSource is not provided |
| files.originalSource | OriginalSource | string | Dependant | Public URL of the file. Required if Id is not provided |
| files.contentType | MediaContentType | enum | Optional | e.g. IMAGE, VIDEO, MODEL_3D, EXTERNAL_VIDEO |
| files.alt | Alt | string | Optional | |
| files.filename | Filename | string | Optional | |
| files.duplicateResolutionMode | DuplicateResolutionMode | enum | Optional | APPEND_UUID, RAISE_ERROR or REPLACE |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Media>
<Record>
<OriginalSource>https://example.com/images/bird-feeder.jpg</OriginalSource>
<MediaContentType>IMAGE</MediaContentType>
<Alt>Bird feeder</Alt>
</Record>
</Media>
</Product>
</Products>
Options
The product options (maximum of 3). Options must be provided whenever variants are provided, and every variant's OptionValues must use these option names and values.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| productOptions.id | Id | string | Optional | GraphQL ID of an existing option |
| productOptions.name | Name | string | Required | e.g. Colour |
| productOptions.position | Position | int | Optional | |
| productOptions.values.name | Values/Value | string | Required | One Value element per option value |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Options>
<Option>
<Name>Colour</Name>
<Position>1</Position>
<Values>
<Value>Red</Value>
<Value>Blue</Value>
</Values>
</Option>
</Options>
</Product>
</Products>
Variants
Variants are matched to existing variants by Id, otherwise by their OptionValues.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| id | Id | string | Optional | GraphQL ID of an existing variant, e.g. gid://shopify/ProductVariant/123 |
| sku | Sku | string | Optional | |
| barcode | Barcode | string | Optional | |
| price | Price | decimal | Optional | |
| compareAtPrice | CompareAtPrice | decimal | Optional | |
| taxable | Taxable | bool | Optional | |
| inventoryPolicy | InventoryPolicy | enum | Optional | DENY or CONTINUE (continue selling when out of stock) |
| position | Position | int | Optional | |
| requiresComponents | RequiresComponents | bool | Optional | |
| file | Media/Record | - | Optional | Only the first Record is used. Accepts the same fields as the product Media |
| metafields | Metafields/Record | - | Optional | See Metafields |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Variants>
<Record>
<Id>gid://shopify/ProductVariant/42309202509913</Id>
<Sku>BF0001-RED</Sku>
<Barcode>5012345678900</Barcode>
<Price>8.00</Price>
<CompareAtPrice>10.00</CompareAtPrice>
<Taxable>true</Taxable>
<InventoryPolicy>DENY</InventoryPolicy>
<Position>1</Position>
<Media>
<Record>
<OriginalSource>https://example.com/images/bird-feeder-red.jpg</OriginalSource>
<MediaContentType>IMAGE</MediaContentType>
</Record>
</Media>
</Record>
</Variants>
</Product>
</Products>
Option Values
The option values that identify the variant. Provide one OptionValue per product option.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| optionValues.optionName | OptionName | string | Dependant | Required if OptionId is not provided |
| optionValues.optionId | OptionId | string | Dependant | Required if OptionName is not provided |
| optionValues.name | Name | string | Dependant | Required if Id and LinkedMetafieldValue are not provided |
| optionValues.id | Id | string | Dependant | GraphQL ID of an existing option value. Required if Name and LinkedMetafieldValue are not provided |
| optionValues.linkedMetafieldValue | LinkedMetafieldValue | string | Dependant | Only for options linked to a metafield |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Variants>
<Record>
<OptionValues>
<OptionValue>
<OptionName>Colour</OptionName>
<Name>Red</Name>
</OptionValue>
</OptionValues>
</Record>
</Variants>
</Product>
</Products>
Inventory Item
The inventory item linked to the variant.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| inventoryItem.sku | Sku | string | Optional | |
| inventoryItem.tracked | Tracked | bool | Optional | Whether Shopify tracks inventory for the variant |
| inventoryItem.requiresShipping | RequiresShipping | bool | Optional | |
| inventoryItem.cost | UnitCost/Amount | decimal | Optional | |
| inventoryItem.countryCodeOfOrigin | CountryCodeOfOrigin | string | Optional | Two letter ISO country code, e.g. GB |
| inventoryItem.provinceCodeOfOrigin | ProvinceCodeOfOrigin | string | Optional | |
| inventoryItem.harmonizedSystemCode | HarmonizedSystemCode | string | Optional | |
| inventoryItem.countryHarmonizedSystemCodes.countryCode | CountryHarmonizedSystemCodes/Record/CountryCode | string | Optional | |
| inventoryItem.countryHarmonizedSystemCodes.harmonizedSystemCode | CountryHarmonizedSystemCodes/Record/HarmonizedSystemCode | string | Optional | |
| inventoryItem.measurement.weight.unit | Measurement/Weight/Unit | enum | Optional | GRAMS, KILOGRAMS, OUNCES or POUNDS |
| inventoryItem.measurement.weight.value | Measurement/Weight/Value | decimal | Optional |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Variants>
<Record>
<InventoryItem>
<Tracked>true</Tracked>
<RequiresShipping>true</RequiresShipping>
<UnitCost>
<Amount>4.00</Amount>
</UnitCost>
<CountryCodeOfOrigin>GB</CountryCodeOfOrigin>
<HarmonizedSystemCode>442190</HarmonizedSystemCode>
<CountryHarmonizedSystemCodes>
<Record>
<CountryCode>US</CountryCode>
<HarmonizedSystemCode>4421900000</HarmonizedSystemCode>
</Record>
</CountryHarmonizedSystemCodes>
<Measurement>
<Weight>
<Unit>KILOGRAMS</Unit>
<Value>0.25</Value>
</Weight>
</Measurement>
</InventoryItem>
</Record>
</Variants>
</Product>
</Products>
Inventory Quantities
Stock levels to set for the variant at one or more locations. If LocationId is not provided, the location is looked up by LocationName, and the record fails if no location is found.
| Shopify Field | XML Field | Type | Input | Notes |
|---|---|---|---|---|
| inventoryQuantities.locationId | LocationId | string | Dependant | Required if LocationName is not provided, e.g. gid://shopify/Location/123 |
| - | LocationName | string | Dependant | Required if LocationId is not provided |
| inventoryQuantities.name | Name | string | Required | available or on_hand |
| inventoryQuantities.quantity | Quantity | int | Required |
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Variants>
<Record>
<InventoryQuantities>
<InventoryQuantity>
<LocationId>gid://shopify/Location/16144367705</LocationId>
<Name>available</Name>
<Quantity>6</Quantity>
</InventoryQuantity>
<InventoryQuantity>
<LocationName>Newcastle</LocationName>
<Name>available</Name>
<Quantity>10</Quantity>
</InventoryQuantity>
</InventoryQuantities>
</Record>
</Variants>
</Product>
</Products>