Product resource

Products and its associated resources are one of the more commonly used interfaces in the API. This section documents the main Product resource, and the numerous related resources which are used to flesh out & configure a product in ZEST.

The primary resource is called Product (either works), and stores the main product entry. However to get a full picture of a product's data, you will need to work with various related resources. These resources include:

Product fields

The main product fields you may want to interact with include:

  • sku - the primary key for a product entry.
  • title - the main title shown whenever the product is rendered.
  • description - a short description sometimes used when a summary of the product is output.
  • comment - the primary description that is output on the product page.
  • faqs - output on the main product page in supporting websites.
  • thumb - a thumbnail image that is used in various places when outputting a summary of the product.
  • image - the main product image shown on the product page.
  • image_large - the default "zoom image" used on the product page.
  • price - the price of the product - if using simple pricing (as in no pricing tiers, quantity pricing etc).
  • advanced_pricing - if set to true, this product will use PricingAdvanced entries to calculate pricing. See that resource for more information.
  • weight - used in some shipping calculations.
  • volume - occasionally used in shipping calculations.
  • option_type - used for products that have options. Either left empty (for a product without options) or set to Simple or Matrix. See the [Options resource] below for more information.
  • options_advanced when set to true in combination with option_type=Matrix a product will be set to used "advanced options". See the [Options resource] below for more information.
  • inactive - if inactive the product will be hidden from the website.
  • gtin, brand and mpn - generally used for product feeds (e.g. Google Shopping), or product filtering.
  • meta_* fields - used for search engine metadata.
  • url_override or slug - assuming the website is not a very old site, url_override is now generally deprecated/ignored, and slug will represent the website permalink for this product. E.g. a slug of my-product will have a URL of https://www.clientwebsite.nz/my-product.
  • external_id - a field you can set if this product needs to relate to a product code in another piece of software. E.g. if integrating to an inventory system, this may be the product code in the inventory system.
  • custom_data1 to custom_data4 - these are blank fields that are not used by our software, and exist for 3rd party developers needing to store additional adhoc data against a product. Feel free to use them - just be sure they're not being used by another integration.

There are other fields, but these often have specialist uses that are unlikely to affect your app. You can learn more about them by browsing to the product resource in your web browser, as documented in the discoverability section.

Example - creating a product

We can use the following request to create an example product, set its inventory, and put it into two categories. Remember - we only have to specify the fields we care about setting:

POST /API/V3/Product+Inventory+ProdCat
<ResultSet>
    <Product>
        <sku>foo-sku</sku>
        <title>Foo Dummy Product</title>
        <comment>Detailed description of this foo dummy product</comment>
        <external_id>81</external_id>
        <price>120.00</price>
        <image>foo-image.jpg</image>
        <slug>foo-dummy-product</slug>
        <Inventory>
            <sku>foo-sku</sku>
            <quantity>28</quantity>
        </Inventory>
        <ProdCat>
            <cat_code>foo-category-code</cat_code>
            <sku>foo-sku</sku>
        </ProdCat>
        <ProdCat>
            <cat_code>bar-category-code</cat_code>
            <sku>foo-sku</sku>
        </ProdCat>
    </Product>
</ResultSet>

Related resources

Product data is stored across a number of resources, so understanding the various resources and how they relate to each other is important to being able to effectively work with this area.

The main resources related to a product include:

ExtraImages 1:M

By default a single product image is stored in the Product resource - along with a thumbnail & large version of that image. However very often we want to store multiple images of a product. Additional images are stored as entries in the ExtraImages resource.

The main fields for this resource include:

  • code - key - any unique string (up to 25 characters long)
  • sku - the Product sku this entry relates to
  • image - the filename of the full size image that should be used. This image should be stored in the images/items/ folder
  • thumbnail - the filename of the thumbnail image. This image should be stored in the images/thumb/ folder

Inventory 1:1

For most websites it is important to know how much inventory a product has available. Some websites are configured to ignore inventory, but most will automatically disable purchasing when an item goes out of stock.

If you are keeping inventory levels for a product in sync with an inventory management system or ERP, you can write directly to this resource & generally not have to touch the Product resource unless you want to update other product data.

However if you are creating products, or reading product data you can include the Inventory resource in your resource specification.

The key fields for this resource include:

  • sku - the product SKU this entry relates to.
  • quantity - how many are in stock.
  • stock_message - a custom message to show on the website if it goes out of stock.

Merchandising 1:1

When a product is put on promotion, an entry is added to the Merchandising resource. A promtion may include a discount, and/or a label to add to the product card where it is output on the website (e.g) category/search results, featured products, cross sells etc.

Relevant fields include:

  • sku - the product SKU this entry relates to
  • discount - a discount amount to apply - e.g. 40% or for a numeric discount 30.
  • featured - a feature label to add to product cards
  • timed_promotion - does this promotion apply only to a specific date range?
  • start_date - if this is a timed promotion, when should it start
  • end_date - if this is a timed promotion, when should it end

PricingAdvanced 1:M

If a product needs more than the most basic pricing levels, then the pricing_advanced field on the Product resource is set to true, and entries in the PricingAdvanced resource are used to determine the price for a given situation.

This resource allows a grid/matrix of pricing to be defined for a product, adding support for one or more of the following dimensions:

  • Pricing tiers (pricing by customer group).
  • Quantity pricing.
  • Prices for different currencies.

Relevant fields that you can set for each entry include:

  • sku - mandatory - the product SKU this entry relates to
  • start - mandatory - a start quantity break. Defaults to 0. The start should never overlap the end of another entry with the same group_code and currency_code.
  • end - mandatory - an end quanity break. Defaults to 9999999 which is considered to be "infinity".
  • price - mandatory - the price that should be charged
  • group_code - optional - only required if you wish to assign this range to a particular customer group (see the UserGroups resource). Leaving this empty will specify that this price should be the "default" price used for all visitors who do not match another pricing tier (including anonymous visitors who have not logged in).
  • currency_code - optional - only required if you wish to explicitly define a price for a particular currency

Each of the "dimensions" above adds another dimension to the pricing matrix. It is important that you include entries into the PricingAdvanced resource to cover off all dimensions. For example if you define multiple quantity breaks (one of your entries has an end that is not 9999999), as well as different price tiers (by specifying a group_code for one of your entries), then you need to ensure you specify an entry for every scenario in that 2-dimensional grid.

So if you had:

  • 3 quantity breaks: 0-10, 11-20, 21-9999999
  • 3 pricing tiers: default (empty), dealer, and vip

... then you would need 9 entries to cover all scenarios.

Be careful not to blow up the clients website

The PricingAdvanced resource provides a lot of flexibility for product pricing, but things can very quickly blow out due the exponential nature of its required entries. The more pricing entries ZEST has to process, the more intensive things like category pages and search results are to load due to the number of pricing lookups they need to do.

If you have a situation that will result in a large number of pricing entries, please get in touch with us to discuss the best way this can be done without blowing up the client's website.

Clearing existing pricing data

If you are updating PricingAdvanced entries for a product, a common mistake can be to just insert the records you require. However you need to think about the existing records that are already there.

If you are just attempting to update a single entry, you could just POST an update to that existing entry. However to do this you will need to know the uid value for that specific entry. Simply posting a payload to the /PricingAdvanced resource that doesn't contain a uid will insert a new entry - resulting in duplicate pricing records. The API will accept this, but it can result in unpredictable pricing behaviour in the website.

In most scenarios, we are wanting to update the pricing for a whole product. In this case, it is usually simpler to clear any existing pricing data by issuing a:

DELETE /API/V3/PricingAdvanced?sku=foo-sku

... where foo-sku is the product SKU who's pricing you are updating. You now have a clean slate to POST a new payload containing all PricingAdvanced entries for the product.

NOTE: If you are updating pricing for a lot of products, it will be much more efficient to batch up these deletes. You can delete entries for multiple SKUs by chaining on additional skus into the querystring:

DELETE /API/V3/PricingAdvanced?sku=fook-sku&sku=bar-sku&sku=baz-sku

We typically batch up deletes into groups of 50 skus (assuming your SKU lengths are massive).

Multicurrency

Dealing with explicit currency prices is the exception where you can have missing entries. If an entry is missing for a particular currency, ZEST will auto-calculate the price by fetching the default currency price, and then adjusting it relative to the current exchange rate for the selected currency.

This means in the scenario above, if your website also supported NZD, USD, you could choose to selectively force the USD price for the 11-20 price range for a dealer by creating a single entry for that situation. This would now result in 10 total entries - 9 for the default (e.g. NZD) currency, and 1 for the single override. The rest of the USD prices would auto-calculate by applying the exchange rate to the NZD price.

ProdAccessGroup 1:M

If restricting product access is enabled for a website, this resource can be used to define which UserGroups have access to this specific product.

Entries are a simple match between:

  • sku - the product SKU this entry relates to
  • group_code - the UserGroups code that should be able to access this SKU.

ProductAttributes 1:M

This resource allows you to define additional adhoc data against a product. Attributes are output on the product page in a grid, and can also be used for filtering when browsing categories or with search results.

Each entry should contain:

  • sku - the product SKU this entry relates to
  • label - the label of what we are defining - e.g. "Material"
  • value - the value for the label - e.g. "Aluminium"
  • sort - what order this should be output on the product page

Options 1:M

Options are similar to attributes, but allow a visitor to select from a range of predefined values to configure a product they are looking to purchase.

Common examples include colour, or size - but options are completely adhoc, allowing you to create option values for anything you need configured to allow the merchant to ship the correct product.

Usually if your client is requiring options for their products, it is highly recommended to manually set up all products using the website admin tool (or spreadsheet importers) and then use your app to keep certain data in sync.

Integrations in the past that have attempted to automate option creation for products have tended to be quite fragile as the setup process needs to be precise. Once the product is loaded, you then use an integration/app to keep relevant aspects of those products (pricing, inventory etc) in sync.

This resource is one of the most confusing in the Data API. Numerous fields need to be formatted correctly or the options will not render correctly on the product page. A description of the fields is as follows:

  • code - primary key - any unique string (up to 64 characters).
  • sku - the product SKU this entry relates to.
  • o_group - the underlying name or code for the option that will be saved against the orderline - e.g. colour.
  • o_label - the label that will be shown when rendering this option (e.g. on the product page) - e.g. Colour.
  • o_value - a comma-separated list of valid options - with each option being of the format <value>=<label>. E.g. R=Red,G=Green,B=Blue.
  • o_widget - how this should be rendered on the product page. Generally select is the best option, although you can choose radio buttons by setting this value to breaking_radio_preselect.
  • price - optional - only relevant to simple options, and only required if you would like to adjust the price for certain entries. Set this field to a comma separate list of adjustments you would like to apply, each adjustment of the format <value>=<adjustment>. The adjustment should be a positive or negative integer. You do not need to include values in the list if they don't adjust the price. So if we wanted to adjust the price to be $5 more expensive for Blue, and $2 less expensive for Green, using the o_value example above, you would set this field to: B=5,R=-2
  • o_sort - if there are multiple options for this sku, this will dictate the order in which they appear.

Option types

ZEST supports three types of options:

Simple Options

Simple options allows the visitor to configure a product, and the merchant to collect additional information about a product being purchased.

All options defined will be rendered on the product page (using the provided label, widget & values). When the product is added to the shopping cart, users selections will be saved (and shown in the cart), and finally recorded against the orderline when a checkout is made.

You can optionally do simple adjustments to price (plus or minus values) based on what option is selected, but the same single product SKU will be purchased for each order - meaning there is a single inventory level, single pricing etc for all options. However it is a good option for a lot of merchants who don't need to track inventory levels too closely - keeping complexity to a minimum.

To set a product to use simple options, set option_type to Simple.

Advanced Options

Advanced options extends simple options - providing the same capability to define configurable option, but allowing the merchant to have a different underlying sku associated with each combination of values - known in ZEST as Variants. The combination of selected options will determine which Variant is going to be purchased in the order. Variants can have additional product data specified against them (pricing, inventory, title, short description, imagery etc) - which allows for e.g. certain combinations to go out of stock.

To use advanced options, you also need to create relevant entries in the VariantOptions resource - connecting each SKU to a variant code, and a Variant entry for each combination of options.

This can lead to a lot of entries. If we were to have a product with 3 sizes, and 3 colours - this would require 9 Variant entries, and 18 VariantOption entries. If your app really needs to auto-generate these entries, then the easiest way to determine how to do it is probably to set up a product using the ZEST website admin, and then inspect these resource to see what entries were generated.

To set a product to use advanced options, aside from the entries to the resources mentioned in this section, set the following in your Product node:

<option_type>Matrix</option_type>
<options_advanced>true</options_advanced>
Subproducts

Subproducts skips out the option setup, and instead just groups a number of skus (Variants) under a main product SKU.

All variations are then listed on the product page, allowing the user to select which one they wish to purchase.

To set a product to use subproducts, set the following in your Product node:

<option_type>Matrix</option_type>
<options_advanced>false</options_advanced>

Variants 1:M

Variants represent variations of a product that are represented by a physical item - and so generally have their own code/sku, inventory levels and sometimes their own pricing.

They are used when a product is set to use advanced options or subproducts.

Key fields for this resource include:

  • code - primary key - a unique product or variation code - usually the SKU or code of this physical product.
  • sku - the product SKU this entry relates to.
  • price - the price for this variation
  • advanced_pricing - as with products, any variant can also have a full matrix of pricing defined in the PricingAdvanced resource. If this is set to true, the PricingAdvanced rules will be used instead of the defined price.
  • image - if specified this image will be added into the list of images for a product, and the product image will automatically switch to this when this option is chosen.
  • description - a short description / title. This is shown on the product page for subproducts, and in the shopping cart / order if the variant is purchased.
  • weight - used for shipping calculations
  • volume - occasionally used for shipping calculations
  • sort - if a product is using subproducts, this will determine the display order on the product page.
  • external_id - a field you can set if this variant needs to relate to a product code in another piece of software. E.g. if integrating to an inventory system, this may be the product code in the inventory system.
  • custom_data1 to custom_data4 - these are blank fields that are not used by our software, and exist for 3rd party developers needing to store additional adhoc data against a variant. Feel free to use them - just be sure they're not being used by another integration.

Variant Related Resources

Because Variants operate like products, a few of the related product resources can also relate to a variant. These include:

  • Inventory - used to define inventory levels for a specific variant.
  • AdvancedPricing - used to define pricing rules when advanced_pricing has been set to true against the variant.

Be aware that if you start to define AdvancedPricing rules for individual variants, the number of entries in this resource can very quickly balloon due to the exponential nature of the records required. For example if you have 100 products, and each use advanced options with 2 options, each with 3 values - you now have 600 variants that must be maintained.

This is achievable with an app keeping everything in sync. However if you then have 3 price tiers - your 100 products now have 1800 PricingAdvanced entries that need to be maintained. If you throw in 3 quantity breaks for each product you're now up to 5400 entries, without looking at multicurrency...

The more pricing entries ZEST has to process, the more intensive things like category pages and search results are to load due to the number of pricing lookups they need to do.

If you have a situation that will result in a large number of pricing entries, please get in touch with us to discuss the best way this can be done without blowing up the client's website.

Cat 1:M

The Cat resource represents a product category. If you join this resource to a product, it will list all categories this product is in.

Key fields for this resource include:

  • code - primary key - a unique string (up to 64 characters long).
  • name - the name of the category.
  • header_image - a lot of websites use this image to output behind the category name or in the header of the website when showing this category page. This image should be uploaded to images/originals/.
  • image - an optional image that appears next to the description on the category page, or is sometimes used in menu systems. This image should be uploaded to images/items/.
  • description - an optional description that is output on the category page (depending on website configuration).
  • url_override or slug - assuming the website is not a very old site, url_override is now generally deprecated/ignored, and slug will represent the website permalink for this product. E.g. a slug of my-product will have a URL of https://www.clientwebsite.nz/my-product.
  • meta_* fields - used for search engine metadata.
  • hide - set to true if you want this category to be hidden from any website navigation.

CatParent M:M

Categories can be related to each other in a parent-child relationship via the many-to-many CatParent table to form a category tree. Categories can also exist in multiple categories.

To define these relationships, you enter an entry into this table and define the following fields:

  • cat_code - the code for the category we are wanting to define parents for.
  • parent_code - the code of its parent.
  • sort - what order within the parent this category should appear (in filters, auto-generated menus etc).

Example - creating a category

You can create a category:

POST /API/V3/Cat
<ResultSet>
    <Cat>
        <code>foo-category</code>
        <name>Foo Category</name>
        <slug>foo-category</slug>
    </Cat>
</ResultSet>

... and then relate it to an existing parent category (0 to make it a top level category):

POST /API/V3/CatParent
<ResultSet>
    <CatParent>
        <cat_code>foo-category</cat_code>
        <parent_code>0</parent_code>
        <sort>1</sort>
    </CatParent>
</ResultSet>

ProdCat M:M

Used to put a product into one or more categories. This is mainly used for creating new products & putting them into a category. See the example above.

When retrieving a product and its categories, apps often join to the Cat resource instead - which will provide both the category code and its name (along with other category data) for each category the product is in.

Key fields are:

  • sku - the SKU of the product being put into a category.
  • cat_code - the code of the category the product is in.
  • sort - where on the category page the product should appear.