# What is Stockeo?

Stockeo is a Shopify app that automatically updates your inventory and product details based on data provided by your suppliers. Connect your supplier feeds, map data fields to Shopify properties, and let Stockeo keep your store up to date — on a schedule or on demand.

## How it works

{% stepper %}
{% step %}

#### Connect your supplier feed

Provide the URL of your supplier's data file. Stockeo supports **CSV**, **XML**, and **JSON** formats. You can connect via HTTP, FTP, or Email. Google Sheets are also supported — just publish the spreadsheet as CSV.
{% endstep %}

{% step %}

#### Map data fields

Tell Stockeo which fields in the supplier file correspond to Shopify properties like SKU, price, quantity, title, description, images, and more. You can also set up price calculations, quantity formatting, variants, and metafields.
{% endstep %}

{% step %}

#### Set a schedule

Choose how often Stockeo should sync your data — every few hours, once a day, or any custom interval. You can also run syncs manually whenever you need.
{% endstep %}

{% step %}

#### Stockeo keeps your store updated

Each time the automation runs, Stockeo fetches the latest data from your supplier and applies the changes to your Shopify products and inventory.
{% endstep %}
{% endstepper %}

## Key features

* **Multiple feed formats** — work with [CSV](/data-mapping/format/csv), [XML](/data-mapping/format/xml), and [JSON](/data-mapping/format/json) supplier files.
* **Flexible data mapping** — map any field from your feed to Shopify properties including [SKU](/data-mapping/sku), [price](/data-mapping/price-calculation), [quantity](/data-mapping/quantity-settings), [title](/data-mapping/title), [description](/data-mapping/description), [images](/data-mapping/image-url-settings), [variants](/data-mapping/variants), [metafields](/data-mapping/metafields), and more.
* **Price calculations** — apply markups, margins, or formulas to supplier prices before syncing to Shopify.
* **Create or update products** — use Stockeo to update existing inventory or create new products from a feed.
* **Filters** — narrow down which products from the feed should be processed using [filters](/settings/filters).
* **Scheduled automations** — run syncs automatically on a recurring schedule.
* **Multiple connection methods** — fetch feeds over HTTP, FTP, or Email.

## Getting started

Pick a guide that matches your use case:

* [Update inventory from a Google Sheet](/common-use-cases/update-inventory-from-a-google-sheet)
* [Update inventory from an XML feed](/common-use-cases/update-inventory-from-an-xml-feed)
* [Create products from a JSON feed](/common-use-cases/create-products-from-a-json-feed)

Or jump straight to [Data Mapping](/data-mapping/format) to learn how to configure field mappings for your feed.


# FAQ

Frequently Asked Questions

#### What IP address does Stockeo use for FTP connections? <a href="#what-ip-address-does-fulfilleo-use-for-ftp-connections" id="what-ip-address-does-fulfilleo-use-for-ftp-connections"></a>

Stockeo connects to FTP servers from the IP address **138.197.226.212**. If your supplier's server is protected by a firewall, ask them to whitelist this address so that incoming connections from it are allowed.

#### Metafield that I want to update is grayed out on the metafields list

<figure><img src="/files/wJzO7z3A3yBnOHYQxZe7" alt="Metafield selection list in Stockeo showing available and unavailable metafields when configuring automation settings." width="563"><figcaption><p>Stockeo metafields selection list showing available metafields in an automation interface</p></figcaption></figure>

This usually indicates one of two things: either the metafield is already selected in another automation, or its type is not supported by Stockeo.

For more details, please refer to the [**Supported metafield types**](/data-mapping/metafields) section.

#### The automation with the feed hosted on Dropbox doesn't work

A common issue in this case is the URL used in the automation.

By default, the Dropbox share URL redirects to a file preview page embedded in Dropbox. Stockeo requires a direct download link to access the file content.

To fix this, modify the shared Dropbox URL by changing the `dl` parameter at the end of the link from `0` to `1`.

For example:

```
https://www.dropbox.com/scl/fi/7x9k2m1p8qzv0n3a5r8t2/stock.csv?rlkey=123ac456def789&dl=0
```

This URL opens the file in Dropbox preview mode.

To force a direct download, update it to:

```
https://www.dropbox.com/scl/fi/7x9k2m1p8qzv0n3a5r8t2/stock.csv?rlkey=123ac456def789&dl=1
```

This version points directly to the file and allows Stockeo to properly fetch the feed content.


# Update inventory from a Google Sheet

With Stockeo, you can sync the inventory in your Shopify store from a Google Sheet.

{% embed url="<https://www.youtube.com/watch?v=WEjzW2EpYW8>" %}
Update Shopify inventory from a Google Sheet
{% endembed %}

### Publish the spreadsheet as CSV

The first step is to make the spreadsheet available as a CSV file under a link.

In Google Sheets, click on File > Share > Publish to web

<figure><img src="/files/BUOVKp6TQ1WKyOC7g2Ds" alt=""><figcaption></figcaption></figure>

Change the publication format from "Web page" to "Comma-separated values (.csv)", and click Publish.

<figure><img src="/files/kZ5NhiWfmVxqK4HN3FY9" alt=""><figcaption></figcaption></figure>

Copy the file link that appeared after you clicked Publish.

<figure><img src="/files/uKXH6Cc8KydEVfizKERW" alt=""><figcaption></figcaption></figure>

### Set up an automation in Stockeo

Create a new automation in Stockeo.

In the Feed Source section, paste the link you copied in the previous step into the URL field.

<figure><img src="/files/lj5RCPheUPUBs0IORjok" alt=""><figcaption></figcaption></figure>

In the Data Mapping section, switch the format to CSV, and specify which column in the Google Spreadsheet contains SKUs and which one contains quantities. You can also add mapping for other columns if you want to sync other information like cost or price.

<figure><img src="/files/MCWLvKAbhfoHlTvXqNcu" alt=""><figcaption></figcaption></figure>

That's all you need to configure. Make sure to save the settings.

### Run the automation

You can either wait until the scheduled time or run the automation manually. If you want to run it immediately, then go to the Automations page, and click on More actions > Run.

<figure><img src="/files/BG8EhbZvrHl7sTHJuvRj" alt=""><figcaption></figcaption></figure>


# Update inventory from an XML feed

Stockeo can sync the inventory in your Shopify store with your supplier's XML feed.

### Create an automation in Stockeo

Let's start by adding a new Stockeo automation. Navigate to your **Shopify admin panel** → **Apps** → **Stockeo**. Hit **Create Automation**. Type the automation name (or leave the default one), select whether to create or update (this option in our case) products and set when the automation should run.

<figure><img src="/files/WkN4eT2ldBYl5EVZR3xg" alt="How to create new automation in Stockeo app for Shopify animated"><figcaption><p>Create new Stockeo automation</p></figcaption></figure>

In the next step, set where to get the product feed from. Select one of the available channels - HTTP, FTP, or Email. If you are going to use FTP, fill in the Host URL, Username, Password, and File path fields. Once you finished, click Connect.

<figure><img src="/files/RW553NvoJ5kTQ7PssGPv" alt="How to set up feed source in the Stockeo app for Shopify animated"><figcaption><p>Set the feed source</p></figcaption></figure>

Let's move to the Data Mapping section. Start by choosing the file format. Specify the Product Element, i.e. the name of the XML element that wraps product details. Set the SKU and Quantity fields according to the names used in the file from your supplier. You can also add other fields that you would like to update such as Barcode, Compare at Price, Cost Per Item, Price, Title or Weight.

<figure><img src="/files/td0bHCkervF0sYAEEEEc" alt="How to set Data Mapping in the Stockeo app for Shopify animated"><figcaption><p>Set the Data Mapping</p></figcaption></figure>

<figure><img src="/files/1cor3hNFwrOooGmBb4GN" alt="Fields from the supplier&#x27;s file needed in Stockeo data mapping section annotated"><figcaption><p>Fields from your supplier's feed needed in the Stockeo Data Mapping section</p></figcaption></figure>

Set the filters to choose which products should be modified. You can filter by vendor or product tags. Pick also the location for which the quantity will be updated. Finally, save the automation.

<figure><img src="/files/idQuNmmY7eZzrdMnSQMp" alt="How to set filters and location in the Stockeo automation animated"><figcaption><p>Set filters and location</p></figcaption></figure>

### Run your automation

To see how it works, wait for a scheduled action or click **Run** to update products right away. Then navigate to the Stockeo **History** section. If you can see 'In progress', please refresh the page. The update time depends on the feed size and the number of products in your store. When the status is 'Success', you can click on **Details** to check how many and which products have been modified.

<figure><img src="/files/rloL6f0kDBOM6tRkcmBE" alt=""><figcaption><p>Run automation and check a summary</p></figcaption></figure>

If your XML feed has a more complex structure and you need to extract product data from attributes, visit [Enhanced XML Data Mapping](/data-mapping/format/xml#enhanced-xml-data-mapping) to learn more.


# Create products from a JSON feed

Stockeo can create products in your Shopify store from your supplier's JSON feed.

### Add new Stockeo automation

First, add a new Stockeo automation. Navigate to your **Shopify admin panel** → **Apps** → **Stockeo**. Click **Create Automation**. Type the automation name (or leave the default one). In the **Processing Mode** section pick whether to update or create (this option in our case) products. The **Schedule** section allows you to set when the automation should run.

<figure><img src="/files/hqiZqqfVzMAjJcjKpIbA" alt="How to add a new Stockeo automation with the create processing mode animated"><figcaption><p>Add a new Stockeo automation with the create processing mode</p></figcaption></figure>

### Set the connection

Set where to get the product feed from. Pick one of the available channels - HTTP, FTP, or Email. If you are going to use the first option, pick the method (GET or POST) and paste the URL. You can also add headers and select an authentication method. To check if your connection works fine, click on Test.

<figure><img src="/files/4HqijE7720kc6TUFgmw6" alt="How to set up an HTTP connection in Stockeo animated"><figcaption><p>Set up an HTTP connection in Stockeo</p></figcaption></figure>

### Data Mapping

Let's move to the Data Mapping section. Start by choosing the JSON file format. Then, for the subsequent properties, assign the corresponding names from the JSON. You also can decide whether products created in this automation will have Draft or Active status.

{% tabs %}
{% tab title="Stockeo Data Mapping section" %}

<figure><img src="/files/c5uolw8fQMPrxanNfZ4d" alt="How to configure data mapping section in the Stockeo app for Shopify when you want to create products"><figcaption><p>Data Mapping configuration</p></figcaption></figure>
{% endtab %}

{% tab title="JSON feed" %}

```json
{
  "Stock": [
    {
      "Stock_No": "13369A",
      "Availability": "G",
      "Shape": "RADIANT",
      "Weight": "1.15",
      "Color": "F",
      "Clarity": "SI1",
      "Cut_Grade": "G",
      "Polish": "G",
      "Symmetry": "f",
      "Fluorescence_Intensity": "M",
      "Fluorescence_Color": "B",
      "Measurements": "6.05 X 4.94 X 4.44",
      "Lab": "GIA",
      "Treatment": "",
      "FancyColor": " ",
      "Fancy_Color_Intensity": " ",
      "FancyColorOvertone": " ",
      "DEPTH_PER": "89.9",
      "TABLE_PER": "64",
      "Girdle_Min": "Very Thick",
      "Girdle_Max": "Extremely Thick",
      "Girdle_Per": "",
      "Girdle_Condition": "",
      "Culet_Size": "VS",
      "Culet_Condition": "",
      "Crown_Height": "",
      "Crown_Angle": " ",
      "Pavilion_Depth": " ",
      "Pavilion_Angle": "",
      "Cert_Comments": "Surface graining is not shown.",
      "Country": "USA",
      "State": "NY",
      "City": "New York",
      "Country_Of_Origin": "BWA",
      "Key_To_Symbols": "FeatherCrystalIndented NaturalNaturalExtra Facet",
      "Shade": "None",
      "Star_Length": "",
      "Report_Issue_Date": "08/27/2019",
      "Report_Type": "Diamond Grading Report",
      "Milky": "None",
      "Eye_Clean": "Yes",
      "Gemprint_ID": "",
      "BGM": "NO",
      "Ratio": "1.22",
      "Diamond_Type": "Natural Diamond",
      "Member_Comments": " GD MAKE +2% RAP For Memo Service/Free Ship To HK",
      "Time_to_Location": "",
      "LsMatchedPairSeparable": "",
      "Pair_Stock": " ",
      "Allow_Raplink_Feed": "",
      "Parcel_Stones": "",
      "Center_Inclusion": "None",
      "Black_Inclusion": "None",
      "Lab_Location": "",
      "Brand": "",
      "Sarine_Name": null,
      "Internal_Clarity_Desc_Code": "SWVG",
      "Clarity_Description": "Eye Clean White Side",
      "Modified_Rate": "",
      "wire_discount_price": "",
      "ImageLink": "https://dnalinks.in/13369A/still.jpg",
      "VideoLink": "https://dnalinks.in/13369A/video.mp4",
      "Video_HTML": "https://dnalinks.in/13369A/13369A.html",
      "CertificateLink": "https://dnalinks.in/certificate_images/5202542015.pdf",
      "Rap_Price": "5500",
      "Memo_Price": "3190",
      "Memo_Discount_PER": "-42.00",
      "Buy_Price": "3080",
      "Buy_Price_Discount_PER": "-44.00",
      "COD_Buy_Price": "2970",
      "COD_Buy_Price_Discount_PER": "-46.00",
      "Certificate": ""
      }
   ]
}
```

{% endtab %}

{% tab title="Full Description formula" %}

```liquid
{{ "<h3>Product Details:</h3>" }} {{ "<b>Shape:</b>" }} {{ Shape | downcase | append: "<br/>" }} {{ "<b>Cut Grade:</b>" }} {{ Cut_Grade | append: "<br/>" }} {{ "<b>Color:</b>" }} {{ Color | append: "<br/>" }}// Some code
```

{% endtab %}
{% endtabs %}

It can be that there is no product title in the feed. However, the title is required by Shopify for creating a product. In this case, use the Liquid formula to combine properties from the feed to create a[ product title](/data-mapping/title). Similarly, with the Liquid formula, you can create an [SKU](/data-mapping/sku#generate-sku-with-the-liquid-formula) and [description](/data-mapping/description).

The feed above does not contain the number quantity, but the text. Find more on [how to transform a text quantity to number](/data-mapping/quantity-settings#transform-text-to-number).

You can also decide whether the products created should have a draft or active status. If you choose neither, by default the products will be the draft.

<figure><img src="/files/I2rzruiBIpg3ezXvSHuz" alt="How to select status for products to be created animated annotated"><figcaption><p>Select status for products to be created</p></figcaption></figure>

### Speed up the automation using filters

If you have or plan to have products from other suppliers then a good practice is to add a fixed tag for this supplier, and also set a filter on this tag.

<figure><img src="/files/R2H90qJFj1TDNcdKlbRM" alt="Set tag and filter for products annotated"><figcaption><p>Set tag and filter for products</p></figcaption></figure>

This will speed up processing, and make it easier to bulk edit or delete the products in case there would be something wrong with the imported data.

### Run your automation

To see how it works, wait for a scheduled action or click **Run** to create products right away. Then navigate to the Stockeo **History** section. If you can see '**In progress**' status, please refresh the page. The creation time depends on the feed size and the number of products. When the status is '**Success**', you can click on **Details** to check how many and which products have been created. Click an SKU to see the created product.

<figure><img src="/files/XopYwVhjgV4D0XVQpGUL" alt="Run the Stockeo automation animated annotated"><figcaption><p>Run the Stockeo automation and check the result</p></figcaption></figure>


# Format

Stockeo supports the following input formats:

* [CSV](/data-mapping/format/csv)
* Excel
* [JSON](/data-mapping/format/json)
* [XML](/data-mapping/format/xml)


# CSV

CSV (Comma-Separated Values) is a popular file format used for storing and exchanging tabular data.

<figure><img src="/files/Hwa2LOIfFZhFLh1tbAvY" alt="CSV settings"><figcaption><p>CSV settings</p></figcaption></figure>

You can fine-tune importing data from a CSV file with the following options:

### Encoding

Specify the file encoding as one of:

* UTF-8
* UTF-16
* Win-1250
* Win-1251
* Win-1252
* Win-1253

The default encoding is UTF-8.

### First row is a header

This checkbox option indicates whether the first row of the CSV file contains column names.

When checked, the app will treat the first row as headers, enabling you to map the data columns to their corresponding fields based on these names.

When unchecked, the app will interpret all rows as data. You'll need to reference particular columns by uppercase letters A, B, C, D, E, and so on.

### **Ignore inconsistent number of columns**

This checkbox allows you to instruct the app to ignore rows in the CSV file that do not have the same number of columns as the header row. This can be useful if some rows have missing or extra data.

### Start from line

This field allows users to specify the line number from which the app should start processing the CSV file. The default value is "1", which means processing will begin from the first line of the file.


# JSON

JSON is a data format commonly used by APIs.

<figure><img src="/files/YFPy22jSSdZiIUvGH3lC" alt=""><figcaption><p>JSON settings</p></figcaption></figure>

### Products List Path

The key information that Stockeo needs to know is where the list of products is located in a JSON structure.

#### Top level array - empty path

Very often the list of products is at the top level. You can recognize it by the fact that the JSON starts and ends with square brackets `[ ]`.

```json
[{
    "sku": "E0001",
    "qty": 12,
},{
   "sku": "E0002",
   "qty": 23,
}, ... ]
```

In such a case you can leave the Products List Path empty.

#### Object property

Sometime the list is placed under an object property. Then you need to specify the property name in the Products List Path.

```
{
    "total_count": 7532,
    "items": [{
            "sku": "E0001",
            "qty": 12,
        },{
           "sku": "E0002",
           "qty": 23,
        }, 
        ... 
     ]
}
```

For the example above, the Products List Path should be `items`.

#### Nested path

Rarely, the list of products may be nested within an inner object that involves traversing multiple levels of objects to reach the desired data. In such a case you need to specify the full path, that is, a list of properties separated by dots, without any spaces between them.

```
{
    "status": "success",
    "data": {
        "count": 7532
        "items": [{
                "sku": "E0001",
                "qty": 12,
            },{
               "sku": "E0002",
               "qty": 23,
            }, 
            ... 
        ]
    }
}
```

For the example above, the Products List Path should be `data.items`.

### Variants Path

Use this field only when product variants are nested within the main product object.

{% hint style="info" %}
If variants are defined as separate product objects and share a common value (such as product name, parent SKU, or parent ID), this field should remain empty.

For additional details, refer to the [Variants](https://help.stockeo.solvenium.com/data-mapping/variants) section.
{% endhint %}

```json
{
  "products": [
    {
      "id": 8237461928374,
      "title": "Basic T-Shirt",
      "variants": [
        {
          "id": 4839201746501,
          "title": "Black / M",
          "option1": "Black",
          "option2": "M",
          "option3": null,
          "sku": "TSHIRT-BLK-M",
          "price": "79.99"
        },
        {
          "id": 4839201746502,
          "title": "White / L",
          "option1": "White",
          "option2": "L",
          "option3": null,
          "sku": "TSHIRT-WHT-L",
          "price": "79.99"
        }
      ]
    }
  ]
}
```

For the JSON above, enter `variants` in the Variants Path field.

<figure><img src="/files/2rPmuuvpuHtQywivloe0" alt="Variants path settings for JSON feeds in Stockeo automation"><figcaption><p>Variants Path settings for JSON feeds in Stockeo</p></figcaption></figure>


# XML

XML is a flexible, text-based format used for structuring and storing data, commonly used in web services and data exchange between systems.

<figure><img src="/files/x1DZMbBwSXk9z0uNFYuo" alt=""><figcaption><p>XML settings</p></figcaption></figure>

## Product Element

The key information required by Stockeo is the node name that contains the data for a single product.

```html
<Products>
    <Product>
        <Code>0260</Code>
        <Title>Sitting Jazz Band Saxophonist</Title>
        <Price>13.73</Price>
        <RRP>32.95</RRP>
        <AvailableStock>181</AvailableStock>
        <Barcode>5050140026007</Barcode>
        <Description>A sitting jazz man, playing the saxophone.</Description>
        <Image>https://www.hill-interiors.com/images/giant/0260.jpg</Image>
    </Product>
    <Product>
        <Code>0261</Code>
        <Title>Sitting Jazz Band Trumpeter</Title>
        <Price>13.73</Price>
        <RRP>32.95</RRP>
        <AvailableStock>162</AvailableStock>
        <Barcode>5050140026106</Barcode>
        <Description>Bring timeless musical elegance to any setting with the Sitting Jazz Band
            Trumpeter.</Description>
        <Image1>https://www.hill-interiors.com/images/giant/0261.jpg</Image>
    </Product>
</Products>
```

In such a case above enter `Product` in the Product Element field.

<figure><img src="/files/hJ7IPC0XzW9uZgZmCtUV" alt=""><figcaption><p>Product Element</p></figcaption></figure>

Fields associated with the product are relative to the node used as Product Element:

* Title
* Description
* Image URL
* Product Type
* Tag
* Vendor
* Product Metafield

## Variant Element

Fill in this field only when variants are nested in the product node.

{% hint style="info" %}
If variants are in separate nodes at the product level and have a node with the same value for all variants of a specific product like product name, parent SKU, or parent ID, leave this field empty. For more information navigate to [Variants](/data-mapping/variants).
{% endhint %}

```xml
<Products>
    <Product>
        <Code>0260</Code>
        <Title>Sitting Jazz Band Saxophonist</Title>
        <Description>A sitting jazz man, playing the saxophone.</Description>
        <Image>https://www.hill-interiors.com/images/giant/0260.jpg</Image>
        <Variants>
            <Variant>
                <VariantCode>0260S</VariantCode>
                <Price>13.73</Price>
                <RRP>32.95</RRP>
                <AvailableStock>181</AvailableStock>
                <Barcode>5050140026007</Barcode>
                <Option>Small</Option>
            </Variant>
            <Variant>
                <VariantCode>0260M</VariantCode>
                <Price>20.59</Price>
                <RRP>32.95</RRP>
                <AvailableStock>56</AvailableStock>
                <Barcode>5050140026008</Barcode>
                <Option>Medium</Option>
            </Variant>
            <Variant>
                <VariantCode>0260L</VariantCode>
                <Price>30.89</Price>
                <RRP>32.95</RRP>
                <AvailableStock>74</AvailableStock>
                <Barcode>5050140026009</Barcode>
                <Option>Large</Option>
            </Variant>
        </Variants>
    </Product>
</Products>
```

In such a case above enter `Variants/Variant` in the Variant Element field.

<figure><img src="/files/s4P8Pjku5G6tDGIjVAyu" alt=""><figcaption><p>Variant Element</p></figcaption></figure>

Fields associated with the variant are relative to the node used as Variant Element:

* SKU
* Barcode
* Compare at price
* Continue selling when out of stock
* Charge tax
* Cost per item
* Market Price
* Price
* Quantity
* Weight
* Variant Metafield
* Option

An example feed mapping, where variant nodes are embedded within product nodes, may look as follows:

<figure><img src="/files/ugSXTAxj7IgSgbT0DY2d" alt=""><figcaption><p>Data Mapping section with Variant Element</p></figcaption></figure>

## Basic Data Mapping

In most XML feeds the data you need is stored as text inside nodes, and mapping it is straightforward - just enter the node name or the path to a nested node.

The examples below use the following XML feed:

```xml
<Product id="3378">
  <SKU>WC-1001</SKU>
  <Title>Wooden Clock With Nickel Details</Title>
  <Category>Clocks</Category>
  <Price currency="EUR">29.99</Price>
  <Stock>443</Stock>
  <Images>
    <Image>https://example.com/images/wc-1001-front.jpg</Image>
    <Image>https://example.com/images/wc-1001-side.jpg</Image>
  </Images>
</Product>
```

In this example the Product Element is `Product`.

### Node name

To get the text content of a child node, enter the node name. For example, to get the product title:

```
Title
```

### Path to a nested node

Use a **slash** (`/`) to navigate deeper. For example, to reach the first image inside `Images > Image`:

```
Images/Image
```

### Pick a specific item from a list with \[index]

When a node appears multiple times (like several `<Image>` nodes), you can pick a specific one by its position. The numbering starts at 1.

For example, to get the **second** image:

```
Images/Image[2]
```

### Read an attribute with @

Some feeds store data in XML attributes rather than as text content. To read an attribute, type the **@** sign followed by the **attribute name**.

For an attribute on the product node itself (e.g. if `<Product id="3378">`):

```
@id
```

For an attribute on a nested node, combine the path with `@`. For example, to get the `currency` attribute from the `<Price>` node:

```
Price/@currency
```

You can also combine a path, an index, and an attribute. For example, if images were stored as `<Image url="..."/>`, you would get the URL of the second image like this:

```
Images/Image[2]/@url
```

{% hint style="info" %}
XML namespaces (prefixes like `xml:`, `iaiext:`, etc.) are automatically stripped by Stockeo. When referencing attributes, use the name without the prefix. For example, `xml:lang` becomes just `lang`.
{% endhint %}

### Reading data from Variant Element attributes and values

In some cases, values need to be retrieved directly from a variant element's attribute or its node value.

```xml
<products>
  <product product_id="2001">
    <prices>
      <price currency="PLN" retail_price="299.99" wholesale_price="249.99" />
      <price currency="EUR" retail_price="69.99" wholesale_price="58.99" />
    </prices>
    <variants>
      <variant v_id="501" sku="TSHIRT-BLK-S" ean="5901234560011">15</variant>
      <variant v_id="502" sku="TSHIRT-BLK-M" ean="5901234560028">20</variant>
      <variant v_id="503" sku="TSHIRT-BLK-L" ean="5901234560035">12</variant>
      <variant v_id="504" sku="TSHIRT-BLK-XL" ean="5901234560042">8</variant>
      <variant v_id="505" sku="TSHIRT-BLK-XXL" ean="5901234560059">5</variant>
      <variant v_id="506" sku="TSHIRT-BLK-XXXL" ean="5901234560066">2</variant>
    </variants>
  </product>
</products>
```

To read an attribute from a variant element, use a Liquid formula with the `_attributes` object and specify the attribute name.

```
{{ _attributes.sku }}
```

To read the value of the variant element node itself, use `value` in the mapping field or in Liquid.

```
{{ value }}
```

A sample mapping for this type of XML is shown below:

<figure><img src="/files/5EK8YdnlGaclsri5O7FQ" alt="Data mapping configuration showing how to read values from Variant Element attributes and element values"><figcaption><p>Data mapping example using Variant Element attributes and values</p></figcaption></figure>

## Advanced Data Mapping with Liquid

For anything beyond the expressions above — such as picking a node by the value of its attribute — use a Liquid template.

{% hint style="warning" %}
Selecting a node by attribute value (e.g. "get the name where language is English") is **not** supported with simple expressions. Use Liquid instead as shown below.
{% endhint %}

Consider the following XML where the product title is available in multiple languages:

```xml
<product id="3378" currency="EUR" code_on_card="01269" type="regular" vat="23.0">
  <names>
    <name lang="eng"><![CDATA[Wooden Clock With Nickel Details]]></name>
    <name lang="hun"><![CDATA[Fa óra nikkel részletekkel]]></name>
    <name lang="pol"><![CDATA[Drewniany zegar z niklowymi dodatkami]]></name>
  </names>
  <stock quantity="443" available_stock_quantity="443"/>
  <images>
    <large>
      <image priority="1" url="https://example.com/images/3378-1.jpg"/>
      <image priority="2" url="https://example.com/images/3378-2.jpg"/>
    </large>
  </images>
</product>
```

### Select a node by attribute value

To extract the product title from the `<Name>` node where the language is `eng`, use the `where` filter:

{% tabs %}
{% tab title="Stockeo setting" %}

<figure><img src="/files/3wzUJ1rUNOsYG8L3fQE9" alt="Select node with a specific attribute in the Stockeo automation"><figcaption><p>Select node with a specific attribute in the Stockeo automation</p></figcaption></figure>
{% endtab %}

{% tab title="Copy this setting" %}

```
{{ names.name | where: "_attributes.lang", "eng" | map: "value" | first }}
```

{% endtab %}
{% endtabs %}

### Select an image by attribute value

To pull the URL of the image with `priority="1"`:

{% tabs %}
{% tab title="Copy this setting" %}

```
{{ images.large.image | where: "_attributes.priority", "1" | map: "_attributes" | map: "url" | first }}
```

{% endtab %}
{% endtabs %}

And for the second image (`priority="2"`):

{% tabs %}
{% tab title="Copy this setting" %}

```
{{ images.large.image | where: "_attributes.priority", "2" | map: "_attributes" | map: "url" | first }}
```

{% endtab %}
{% endtabs %}


# SKU

To recognize products in your store and take appropriate properties for them from the supplier feed, Stockeo needs a unique key for each item. The SKU is most commonly used for this.

#### Basic SKU settings

When your supplier provides the SKU in the feed, you can use this value directly. In the following example, the SKU is in the *Code* node.

{% tabs %}
{% tab title="Stockeo SKU settings" %}

<figure><img src="/files/IEmSGAZp614fUhQJedNA" alt="SKU settings in Stockeo for Shopify"><figcaption><p>SKU settings in Stockeo</p></figcaption></figure>
{% endtab %}

{% tab title="Supplier feed" %}

```xml
<Product>
<Code>192330</Code>
<Title>
Pack Of 3 Energy Saving Candle Bulbs 7W (35W Equiv)
</Title>
<Price>2.25</Price>
<RRP>2.95</RRP>
<AvailableStock>29</AvailableStock>
<Barcode>5050140001004</Barcode>
<Categories>Lighting > Bulbs and Accessories</Categories>
<Colour>WHITE</Colour>
<Material>GLASS</Material>
<Description>
Pack of 3 energy saving candle bulbs wth a power of 7 watts, saving energy. These are 40 watt equivalent bulbs
</Description>
</Product>
```

{% endtab %}
{% endtabs %}

#### Add a prefix to the SKU

Use this option if you would like to add a prefix to your SKU easily in the Create mode or in the Update mode when SKUs in your store contain a prefix which does not appear in the feed SKUs.

<figure><img src="/files/SSXbSP97YXzwsgq4rwgg" alt="Add a prefix to products SKU in Stockeo annotated"><figcaption><p>Add a prefix to products SKU in Stockeo</p></figcaption></figure>

#### Generate SKU with the Liquid formula

It may also happen that no field in the feed can be used as the SKU. In this case, use a combination of several fields to create a unique id for each product. To do this enter the Liquid formula directly in the SKU field:

{% tabs %}
{% tab title="Stockeo SKU settings" %}

<figure><img src="/files/CY2tdhlkMBxq1enk8UIM" alt=""><figcaption><p>Sku settings with a Liquid formula in Stockeo</p></figcaption></figure>
{% endtab %}

{% tab title="Supplier feed" %}

<pre class="language-xml"><code class="lang-xml"><strong>&#x3C;item>
</strong>    &#x3C;id>187&#x3C;/id>
    &#x3C;model>PF112&#x3C;/model>
    &#x3C;name>Ernesto leather wallet&#x3C;/name>
    &#x3C;option>Colour Dark Blue&#x3C;/option>
    &#x3C;quantity>34&#x3C;/quantity>
&#x3C;/item>
&#x3C;item>
    &#x3C;id>187&#x3C;/id>
    &#x3C;model>PF112&#x3C;/model>
    &#x3C;name>Ernesto leather wallet&#x3C;/name>
    &#x3C;option>Colour Brown&#x3C;/option>
    &#x3C;quantity>34&#x3C;/quantity>
&#x3C;/item>
</code></pre>

{% endtab %}

{% tab title="Copy SKU settings" %}

```liquid
{{ model }}-{{ option | replace: "Colour ", "" | replace: " ", "-" | upcase }}
```

{% endtab %}
{% endtabs %}

The Liquid formula above for items in this example feed gives the following results:

<figure><img src="/files/4Ml31ySILjbBlM16rvsX" alt="Example SKUs generated with SKU Liquid fomula in the Shopify store"><figcaption><p>Example SKUs generated with SKU Liquid formula</p></figcaption></figure>

#### Alternative variant identifiers

You can also use [metafields](/data-mapping/metafields) or [barcodes](/data-mapping/barcode) to identify variants in Shopify when SKUs are missing from the feed or do not match the SKUs in Shopify. All supported alternative variant identifiers are explained in the following video:

{% embed url="<https://youtu.be/Y-JyC0SsaZQ?si=Bl4EglOt75On_zAx>" %}
Update variants in Shopify by SKU, barcode or metafield
{% endembed %}


# Barcode

Stockeo allows you to update variant barcodes. Barcodes can also be used as an alternative variant identifier when product SKUs are not available in the feed or when SKUs from the feed do not match those in Shopify.

To use barcodes as the variant ID, add the **Barcode** field to the **Data Mapping** section. Then click the gear icon next to the field and enable **Use as variant ID**.

<figure><img src="/files/al7c3aEc3ydSu47OOn8i" alt="Animation showing how to add the Barcode field in Stockeo Data Mapping and enable the “Use as variant ID” option"><figcaption><p>Setting a barcode as an alternative variant ID in Stockeo</p></figcaption></figure>

Once this is done, remove the SKU field from the Data Mapping section.

<figure><img src="/files/ejKYudY3Sst6jp5iosXL" alt="Stockeo Data Mapping section showing the SKU field being removed after setting an alternative variant identifier"><figcaption><p>Removing the SKU field from the Data Mapping section in Stockeo</p></figcaption></figure>

You can also use [metafields](/data-mapping/metafields) to identify variants in Shopify.


# Price

### Price Mapping

Stockeo gives you a possibility to update product prices based on the supplier feed.

Add the Price field in the Data Mapping section, and specify which column/node contains prices in the supplier feed.

<figure><img src="/files/wimJkAH6v2Rrq1ePcefH" alt="price data mapping"><figcaption></figcaption></figure>

### Price Formula

You can also calculate prices according to your needs. Click on the gear icon ![](/files/hsu1KsZLiW1s8kXhwJDk) to define a price formula in Liquid.

<figure><img src="/files/qGGJlEE67aaIIOhBBpao" alt=""><figcaption></figcaption></figure>

### Variables

The available variables are

* `price` - the feed price
* `sku` - the feed SKU
* `quantity` - the feed quantity, available if added to data mapping
* `cost` - the product cost as in the feed, available if added to data mapping
* `compare_at_price`- the product compare at price as in the feed, available if added to data mapping
* `variant.price` - the current variant price in Shopify (before update)
* `variant.cost` - the current variant cost in Shopify (before update)
* `variant.compare_at_price`- the current variant compare\_at\_price in Shopify (before update)
* `product.title`- the current product title in Shopify (before update)
* `product.vendor`- the current product vendor in Shopify (before update)
* `product.tags`- the current product tags in Shopify (before update)

### Operations

You can use all the standard Liquid math filters to perform calculations.

#### plus

Use the `plus` filter to add a number.

```liquid
{{ price | plus: 3 }}
```

#### minus

Use the `minus` filter to subtract a number.

```liquid
{{ price | minus: 1 }}
```

#### times

Use the `times` filter to multiply price by a given number.

```liquid
{{ price | times: 1.2 }}
```

#### **divided\_by**

Use the `divided_by` filter to divide price by a given number.

```liquid
{{ price | divided_by: 1.2 }}
```

#### round

Use the `round` filter to round the price to the nearest integer or to the specified number of decimals.

| Formula                   | Feed Price | Result |
| ------------------------- | ---------: | -----: |
| `{{ price \| round }}`    |       4.32 |   4.00 |
| `{{ price \| round }}`    |       4.56 |   5.00 |
| `{{ price \| round: 1 }}` |       4.44 |   4.40 |
| `{{ price \| round: 1 }}` |       4.68 |   4.70 |

#### ceil

Use the `ceil` filter to round the price up to the nearest integer.

| Formula               | Feed Price | Result |
| --------------------- | ---------: | -----: |
| `{{ price \| ceil }}` |       4.32 |   5.00 |
| `{{ price \| ceil }}` |       4.56 |   5.00 |

#### floor

Use the `floor` filter to round the price down to the nearest integer.

| Formula                | Feed Price | Result |
| ---------------------- | ---------: | -----: |
| `{{ price \| floor }}` |       4.32 |   4.00 |
| `{{ price \| floor }}` |       4.56 |   4.00 |

#### at\_least

Use the `at_least` filter to limit prices to a minimum value.

| Formula                      | Feed Price | Result |
| ---------------------------- | ---------: | -----: |
| `{{ price \| at_least: 5}}`  |       2.34 |   5.00 |
| `{{ price \| at_least: 5 }}` |       5.67 |   5.67 |

### Rounding

You can combine the standard math filters to achieve more price-specific rounding.

#### Round up to .99

Use the following formula to round prices up to the nearest .99

```liquid
{{ price | ceil | minus: 0.01 }}
```

If a price is already a whole number, then the formula will still subtract one cent. If you prefer to keep whole prices as they are, and only round up fractions to .99, then use the following formula.

```liquid
{{ price | ceil | minus: 0.01 | at_least: price }}
```

| Formula                                                | Feed Price | Result |
| ------------------------------------------------------ | ---------: | -----: |
| `{{ price \| ceil \| minus: 0.01 }}`                   |      12.34 |  12.99 |
| `{{ price \| ceil \| minus: 0.01 }}`                   |      12.00 |  11.99 |
| `{{ price \| ceil \| minus: 0.01 \| at_least: price}}` |      12.34 |  12.99 |
| `{{ price \| ceil \| minus: 0.01 \| at_least: price}}` |      12.00 |  12.00 |

### Price formatting

When passing prices to Shopify, it is crucial to ensure they are in the correct format. Prices containing currency codes (e.g., *125 USD*) or commas as decimal separators (e.g., *312,74*) can result in errors such as "Invalid price". To prevent them, you can use Liquid formulas to reformat prices.

#### Removing the currency code from a feed price

Suppose the feed provides a price in the format *125 USD*. To remove the currency code, apply the following Liquid formula:

```liquid
{{ price | remove: "USD" }}
```

Whitespaces are removed from prices by Stockeo under the hood so there is no need to include them in Liquid formulas.

#### Replacing commas with dots in prices for decimal separator

Suppose the feed provides a price in the format *312,74.* To replace the comma with a dot and remove the currency code, apply the following formula:

```liquid
{{ price | replace: ",","." }}
```

#### Removing commas as thousand separators in prices

Suppose the feed provides a price in the format *1,000.* To the comma used as a thousand separator, apply the following formula:

```liquid
{{ price | remove: "," }}
```

{% hint style="info" %}
Replacing a comma with a dot or removing a comma from a price can significantly alter its value if not applied correctly. Make sure to verify the price format used in the feed provided by the supplier.
{% endhint %}

### Add markup to the feed price using metafields

With Stockeo you can shape prices in your store based on the amounts from the feed and markup stored in the metafields for individual products.

In order to access to the metafield use one of the following pattern:

* `product.metafields.namespace.key`
* `variant.metafields.namespace.key`

For example, if you want to add a 50% markup to the product's price, you need to set the markup metafield to 50 and use the following price formula:

```liquid
{% assign markup = product.metafields.custom.markup | default: 0 %}
{% assign markup_multiplier = markup | divided_by: 100 | plus: 1 %}
{{ price | times: markup_multiplier }}
```

The **default** filter prevents error when the metafield with the markup is empty. In such a case the product price will be the same as the amount in the feed.

| Feed Price | Markup Metafield | Calculated Price |
| :--------: | :--------------: | :--------------: |
|    $100    |   no set value   |       $100       |
|    $100    |         0        |       $100       |
|    $100    |        50        |       $150       |
|    $100    |        100       |       $200       |

### Calculate the product price depending on the price level in the feed

If you want the calculation of the price of a product in your store to change depending on the price in the feed, you can use the Liquid formula with **if/elsif** statements.

```liquid
{% if price > 0 and price < 5 %}
{{ price | times: 3 }}
{% elsif price >= 5 and price < 10 %}
{{ price | times: 2 }}
{% elsif price >= 10 and price < 15 %}
{{ price | times: 1.5 }}
{% else %}
{{ price }}
{% endif %}
```

What price does the above code return?

If the price in the feed is between **0** and **4.99**, it will be multiplied by **3**.\
If the feed price is between **5** and **9.99**, it will be multiplied by **2**.\
If the feed price is between **10** and 1**4.99**, it will be multiplied by **1.5**.\
In all other cases, the product price will be equal to the feed price.

### Get price from different columns conditionally

Assume you want to take a price from the column **RRP**. When **RRP** is equal to **0** or **empty**, then get a value from the column **Price**. To handle this case, use the following Liquid code in the Price field:

```liquid
{% assign RRP = RRP | plus: 0 %}{% if RRP == 0 %}{{ Price }}{% else %}{{ RRP }}{% endif %} 
```

<figure><img src="/files/EdZyWjHKQdS0RPmbs3or" alt="If statemant in the price field in the Data Mapping section of Stockeo automation"><figcaption><p><strong>If</strong> statemant in the price field of Stockeo automation</p></figcaption></figure>

The code above is used to select from which column Stockeo should take the price. If you set a **price formula** to calculate the final price, it will be executed on the value that Stockeo selected according to the above condition.

### **Calculate price based on values from different columns**

Assume your feed contains the base purchase price in the `wholesale_price` column and a price multiplier in the `price_multiplier` column. You want Stockeo to calculate the final retail price by multiplying values from these two columns. To handle this case, use the following Liquid code in the Price field:

```liquid
{{ wholesale_price | times: price_multiplier | to_fixed: 2 }}
```

<figure><img src="/files/VO0O4z06qWjDotZ377Jq" alt="Stockeo automation price field configuration showing how product price is set from feed data"><figcaption><p>Stockeo automation setup for the Price field, demonstrating how product price is calculated from imported feed values</p></figcaption></figure>


# Quantity

Stockeo provides extensive options for managing how the quantity from the feed will affect the stock in your store. To change quantity settings, navigate to your **Stockeo Automations** → **Edit** the desired automation → scroll down to the **Data Mapping** section → hit the **gear icon** next to the field in which you typed the name of your supplier's feed quantity. More quantity settings will then appear.

<figure><img src="/files/sya16F6nOwEnowfh1XVD" alt="How to edit advanced quantity settings in the Stockeo app animated"><figcaption><p>Edit advanced quantity settings</p></figcaption></figure>

### Override vs Add

This option allows you to choose whether the feed quantity will overwrite your current stock or if the feed quantity will be added to the current stock.

<figure><img src="/files/ZiNiHufCkXyCwh4l5tXy" alt="Set whether you want to overwrite or add the feed quantity to the stock"><figcaption><p>Set whether you want to overwrite or add the feed quantity to the stock</p></figcaption></figure>

### Quantity formula

You can specify a formula in Liquid to calculate quantity based on the feed quantity.

<figure><img src="/files/TMygvAJDl05TmNeTyDoq" alt="Stockeo quantity formula rounding to full tens."><figcaption></figcaption></figure>

#### Round down to full tens

You can round quantity to full tens using the following formula:

```liquid
{{ quantity | divided_by: 10 | floor | times: 10 }}
```

#### Transform text to number

You can convert a descriptive stock level to a number. For example, the formula below turns *high* to 50, *low* to 10, and *out of stock* to 0.

```liquid
{%- case quantity -%}
{%- when "high" -%} 50
{%- when "low" -%} 10
{%- when "out of stock" -%} 0
{%- endcase -%}
```

#### Calculate with metafields

It's also possible to use a product or variant metafield in the quantity calculation.

* `product.metafields.namespace.key`
* `variant.metafields.namespace.key`

Let's assume you sell products in multipacks, and your provider tracks quantity of individual items. You can keep the multipack size in a variant metafield, and calculate the number of available multipacks with the following formula:

```liquid
{% assign multipack_size = variant.metafields.custom.multipack_size | default: 1 %}
{{ quantity | divided_by: multipack_size }}
```

### Low stock cut-off

In this field you can set the low stock cut-off. If the quantity in the feed is below or equal to the given value, your store's product quantity will be zeroed.<br>

<figure><img src="/files/d0cISHLmC24xtAcg32ly" alt="Stockeo low stock cut-off set to 10"><figcaption><p>Set the low stock cut-off</p></figcaption></figure>

### Reset missing SKUs

If this option is selected, the quantity of products whose SKUs are not present in the feed will be reset to zero. This will be applied to products matching filters.

<figure><img src="/files/1pHW66vus00Eg021efiH" alt="How to reset quantity of products whose SKUs are not present in the feed animated"><figcaption><p>Reset missing SKUs</p></figcaption></figure>

### Force inventory tracking if disabled

<figure><img src="/files/VwdrT7rsFCvYy7sG21Hx" alt="Force inventory tracking if disabled for processed variants"><figcaption><p>Force inventory tracking if disabled</p></figcaption></figure>

When this setting is enabled, if a processed variant has inventory tracking disabled, Stockeo will automatically turn it on.

<figure><img src="/files/xXaCTvx2I5dvdhQusvtr" alt="Enable the inventory tracking option in Shopify"><figcaption><p>Inventory tracking option in Shopify</p></figcaption></figure>

### Enable location if disabled

<figure><img src="/files/jbQboSchmOnwpcYGemSB" alt="Enable location if disabled - Stockeo"><figcaption><p>Enable location if disabled</p></figcaption></figure>

This option enables Stockeo to activate the location specified in the automation for a processed variant if that location is currently disabled.

<figure><img src="/files/TjijeYLYysVPwzEUM1GV" alt="Select location in Stockeo automation"><figcaption><p>Select location in Stockeo automation</p></figcaption></figure>

<figure><img src="/files/s6EOG7RzxXIsdswmyp4P" alt="Stockeo activates selected location"><figcaption><p>Enabling location in Shopify</p></figcaption></figure>

### Quantity format

#### Decimal separator

Comma or point. A comma is interpreted as a decimal separator only if followed by two digits to avoid ambiguity with a comma being used as a thousand separator.

The following notation will be ignored by Stockeo and will not update the stock of this product in your store.

<figure><img src="/files/4jgEquC3CbNxXnwgdFM3" alt="The notation with one or three decimal places that will be ingnored by Stockeo annotated"><figcaption><p>The comma notation of quantity that will be ingnored by Stockeo</p></figcaption></figure>

Shopify only allows quantity to be stored as an integer. When the quantity in the feed is a decimal number, it will be rounded in accordance with the mathematics.

<figure><img src="/files/44hRoDMTsZgOA8CeiJmz" alt="The notation with two decimal places that will be ingnored by Stockeo annotated"><figcaption><p>The comma notation of quantity that will be interpreted by Stockeo</p></figcaption></figure>

The above notation will update the stock to 3 and 4 respectively.

#### Approximate values

Sometimes in the feeds from some suppliers, there is a notation that the quantity of product is greater than a certain value. Stockeo then always sets the stock equal to this value, omitting the greater than and plus signs.

<figure><img src="/files/Z1ehxgoXr6HA41tirigs" alt="Different quantity notations in Stockeo for Shopify annotated"><figcaption><p>Different quantity notations handled by Stockeo</p></figcaption></figure>

All of the notations above are equivalent and will result in the following results:

<figure><img src="/files/5fnuXePpgAsHGhNal33X" alt="Quantity updated by Stockeo according to notations above annotated"><figcaption><p>Quantity updated by Stockeo according to the notations above</p></figcaption></figure>


# Quantity format

#### Decimal separator

Comma or point. A comma is interpreted as a decimal separator only if followed by two digits to avoid ambiguity with a comma being used as a thousand separator.

The following notation will be ignored by Stockeo and will not update the stock of this product in your store.

<figure><img src="/files/4jgEquC3CbNxXnwgdFM3" alt="The notation with one or three decimal places that will be ingnored by Stockeo annotated"><figcaption><p>The comma notation of quantity that will be ingnored by Stockeo</p></figcaption></figure>

Shopify only allows quantity to be stored as an integer. When the quantity in the feed is a decimal number, it will be rounded in accordance with the mathematics.

<figure><img src="/files/44hRoDMTsZgOA8CeiJmz" alt="The notation with two decimal places that will be ingnored by Stockeo annotated"><figcaption><p>The comma notation of quantity that will be interpreted by Stockeo</p></figcaption></figure>

The above notation will update the stock to 3 and 4 respectively.

#### Approximate values

Sometimes in the feeds from some suppliers, there is a notation that the quantity of product is greater than a certain value. Stockeo then always sets the stock equal to this value, omitting the greater than and plus signs.

<figure><img src="/files/Z1ehxgoXr6HA41tirigs" alt="Different quantity notations in Stockeo for Shopify annotated"><figcaption><p>Different quantity notations handled by Stockeo</p></figcaption></figure>

All of the notations above are equivalent and will result in the following results:

<figure><img src="/files/5fnuXePpgAsHGhNal33X" alt="Quantity updated by Stockeo according to notations above annotated"><figcaption><p>Quantity updated by Stockeo according to the notations above</p></figcaption></figure>


# Variants

{% hint style="info" %}
Creation of variants is available only in the Create mode, when you import new products.
{% endhint %}

To group multiple variants into a single product, you need to configure at least two fields:

1. **Variant Group** - a field that has the same value for all variants of a specific product, usually the product name, parent SKU, or parent ID
2. **Variant Option** - a property that distinguishes variants, e.g. color, size, material

If your products have more than one feature that determine variants, you can add more variant options.

{% hint style="info" %}
In JSON and XML formats, if variants are already nested within a product object or product element, you can specify them using the [Variants Path](/data-mapping/format/json#variants-path) (for JSON) or [Variant Element](/data-mapping/format/xml#variant-element) (for XML) instead of the Variant Group.
{% endhint %}

#### Example

Assume you want to import a CSV file with the following columns:

* sku
* name
* color
* size
* price

| sku  | name    | color | size | price |
| ---- | ------- | ----- | ---- | ----- |
| TSRS | T-Shirt | Red   | S    | 25.00 |
| TSRM | T-Shirt | Red   | M    | 25.00 |
| TSRL | T-Shirt | Red   | L    | 25.00 |
| TSGS | T-Shirt | Green | S    | 25.00 |
| TSGM | T-Shirt | Green | M    | 25.00 |
| TSGL | T-Shirt | Green | L    | 25.00 |
| HDBS | Hoodie  | Blue  | S    | 55.00 |
| HDBM | Hoodie  | Blue  | M    | 55.00 |
| HDBL | Hoodie  | Blue  | L    | 55.00 |

To import these items as products with variants, you need the following mapping:

* Title: name
* SKU: sku
* Variant Group: name
* Variant Option 1
  * Name: Color
  * Value: color
* Variant Option 2
  * Name: Size
  * Value: size
* Price: price

<figure><img src="/files/EAKE8EZP5S2VuM1dd30B" alt=""><figcaption><p>CSV data mapping to import variants with color and size options</p></figcaption></figure>

Running the automation on the example CSV file will create 2 products:

* T-Shirt (6 variants)
  * Red / S
  * Red / M
  * Red / L
  * Green / S
  * Green / M
  * Green / L
* Hoodie (3 variants)
  * Green / S
  * Green / M
  * Green / L

<figure><img src="/files/3884Z4gukyhOoKgpHsaJ" alt=""><figcaption><p>Imported product - T-Shirt with 6 variants</p></figcaption></figure>

<figure><img src="/files/8ecPP9t04cuQV3pWOhpt" alt=""><figcaption><p>Imported product - Hoodie with 3 variants</p></figcaption></figure>


# Metafields

You can update product and variant metafields based on the supplier feed.

{% hint style="info" %}
To be able to add a metafield in the Stockeo automation, you need to [add a metafield definition](https://help.shopify.com/en/manual/custom-data/metafields/metafield-definitions/creating-custom-metafield-definitions) in the Custom Data section of your store settings first.
{% endhint %}

### Update metafield single value

Add a Metafield field in the Data Mapping section. Select the metafield to be updated, and specify which column/node contains a corresponding value in the supplier feed.

<figure><img src="/files/jUWqzrrgS1fs124J6PO5" alt="Add Metafield in the Stockeo automation animated"><figcaption><p>Add metafield in the Stockeo automation</p></figcaption></figure>

For example, if you want to store all of the below properties in your store:

```xml
<Item>
<ID>
<![CDATA[ ARC-18126-1-01 ]]>
</ID>
<Width>8.00</Width>
<Size>
<![CDATA[ 18.0 İnch ]]>
</Size>
<Hole>5</Hole>
<PCD>100</PCD>
<Offset>34</Offset>
<Bore>73.1</Bore>
<Stok>15</Stok>
</Item>
```

Use the following settings in your Data Mapping section:

<figure><img src="/files/ywXOzUXATjH5od75rk2t" alt="Data Mapping section of Stockeo automation with added metafields annotated"><figcaption><p>Data Mapping section of Stockeo automation with added metafields</p></figcaption></figure>

It may be the case that the values you want to store in the metafield are nested in the **param** node, while the property **name** and its **value**, are in separate nodes.

```xml
<item>
  <param>
    <name>Brand</name>
    <value>Samsung</value>
  </param>
  <param>
    <name>Model</name>
    <value>UE55TU8500UXXU</value>
  </param>
  <param>
    <name>Screen Size</name>
    <value>55 inches</value>
  </param>
  <param>
    <name>Resolution</name>
    <value>4K Ultra HD</value>
  </param>
  <param>
    <name>Smart TV</name>
    <value>Yes</value>
  </param>
</item>

```

To get them you need to use a Liquid formula in the **Mapping** field with the **where** and **map** filters:

```liquid
{{ param | where: "name", "Brand" | map: "value" }}
```

In this code, **param** refers to a node that contains other nodes with the property name and its value. **"name"** is the name of the node that contains the **"Brand"** property, while **"value"** is the information to be stored in the metafield.

For the feed above use the following settings:

<figure><img src="/files/r73wBamb5a4r0ANN3f0A" alt="How to get the value to be stored in the metafield from the param node"><figcaption><p>Get the value to be stored in the metafield from the param node</p></figcaption></figure>

{% hint style="info" %}
A special use case for metafields is to employ one of them as the product/variant identifier instead of the SKU field.

Check out how to [use a variant metafield to recognize a product from the feed](/data-mapping/sku#use-variant-metafield-to-recognize-a-product-from-the-feed).
{% endhint %}

### Update metafield lists

Stockeo enables you to update metafield lists that allow storing multiple values in a single metafield.

{% hint style="info" %}
Stockeo can only overwrite a current metafield list. There is no mode to add new items and keep the old ones.
{% endhint %}

To pass a list of values to Shopify correctly, ensure commas separate values:

<figure><img src="/files/wRiqD3pC2OtkhfvBhDtO" alt=""><figcaption><p>Commas separated values to be passed as a metafield list</p></figcaption></figure>

If a different separator is used, a semicolon, for instance, add a Liquid formula to convert it to commas:

```liquid
{{ Ingredients | replace: ";","," }}
```

<figure><img src="/files/HjEz0wSHYYDvasYowCAf" alt=""><figcaption><p>Replace semicolons with commas using Liquid</p></figcaption></figure>

You can also use Liquid to pass values from multiple columns/fields:

<figure><img src="/files/gE99AuFM3vC3NkbXaw96" alt=""><figcaption><p>Values to be passed included in separate columns</p></figcaption></figure>

<figure><img src="/files/NkhSMOTpMugqTjUn8Cl5" alt=""><figcaption><p>Use Liquid to pass values from different columns</p></figcaption></figure>

To assign a fixed metafield list of values for each variant, use another Liquid formula:

<figure><img src="/files/oa5ISdsqLkV8rL35JxTp" alt=""><figcaption><p>Use Liquid to pass a fixed values list</p></figcaption></figure>

### Use variant metafield to recognize a product from the feed

In case you do not have in your store the same SKUs of products as in the feed, but you store those SKUs in the metafields for individual products, you can use variant metafields as the key to enable Stockeo to associate product from your store with the product from the feed.

{% tabs %}
{% tab title="SKU and variant metafield" %}

<figure><img src="/files/xoOtQmIVAOF1ViDNNbfp" alt="Shopify example product SKU"><figcaption><p>Shopify product SKU</p></figcaption></figure>

<figure><img src="/files/zXeWPOqwt0BrkeIHenr1" alt="Custom SKU stored as the variant metafield annotated"><figcaption><p>Custom SKU stored as the variant metafield</p></figcaption></figure>
{% endtab %}

{% tab title="Supplier feed" %}

```xml
<Item>
    <ID>
        <![CDATA[ARC-18126-2-29]]>
    </ID>
    <Brand>
        <![CDATA[ARCEO]]>
    </Brand>
    <Price>695.00</Price>
    <Stok>32.00</Stok>
</Item>
```

{% endtab %}
{% endtabs %}

Use the following settings in the Data Mapping section of your automation:

Add a new **Metafield** field:

<figure><img src="/files/ORFTRH4IHvs2kpNwbbIH" alt="Add new metafield field in Stockeo automation animated"><figcaption><p>Add new Metafield field</p></figcaption></figure>

Select the variant metafield with the product SKU that matches the SKU in the feed.

<figure><img src="/files/AHEQKR8HGuP5uU6qPCTf" alt="Select metafield with the custom SKU in the Stockeo automation"><figcaption><p>Select metafield with the custom SKU</p></figcaption></figure>

Enter the name of the feed node where the SKU is stored.

<figure><img src="/files/2uZOPHxUREkNanLxfWKT" alt="Mapping SKU from the feed in the Stockeo automation"><figcaption><p>Mapping SKU from the feed</p></figcaption></figure>

Hit the gear icon. This button will be active if you select your variant metafield. Check the option **Use a variant ID**.

<figure><img src="/files/ZqG17Gsm4Jw5TMNwJl22" alt="Use metafield as variant ID in the Stockeo automation annotated"><figcaption><p>Use metafield as variant ID</p></figcaption></figure>

Delete the default SKU field in your automation. This is possible only if you checked the option above.

<figure><img src="/files/xnosx59HhrCMj3wajDjP" alt="Delete the SKU field in the Stockeo automation animated"><figcaption><p>Delete the SKU field</p></figcaption></figure>

Otherwise, the SKUs of your items will be updated according to the value from the feed.

You can also use [barcodes](/data-mapping/barcode) to identify variants in Shopify when SKUs are missing from the feed or do not match the SKUs in Shopify.

### Metaobjects

Stockeo supports importing data into metafields of type metaobject. When processing your feed, Stockeo matches incoming values to existing metaobject entries in your store using either the **display name** or the **handle**. Please note that matching is case-sensitive.

Stockeo does not create new metaobjects entries. If a value in your feed doesn’t match an existing metaobject, it will be skipped.

***

### Supported metafields types

Stockeo enables you to update values for the following types of metafields:

**Single values**

* Boolean
* Color
* Data
* Date and Time
* Decimal
* Integer
* Metaobject
* Multi-line text
* Single-line text
* URL

**Lists of values**

* Single-line texts
* Decimals
* Integers
* URLs


# Title

To create a product in Shopify the requirement is to give it a title. However, it may happen there is no product name in the feed. Stockeo allows you to create a product title by combining several values from the feed using the Liquid formula.

Assume you want to create a title from some properties from the following JSON:

```json
{
    "Stock_No": "210211",
    "Availability": "G",
    "Shape": "OVAL",
    "Weight": "1.7",
    "Color": "G",
    "Diamond_Type": "Natural Diamond",
    "Clarity": "SI2",
    "Cut_Grade": "VG",
    "Polish": "EX",
    "Symmetry": "VG",
    "Measurements": "9.36 X 6.80 X 4.10",
    "Rap_Price": "7300",
}
```

To do this type a Liquid formula in the **Title** field of your automation. For example, the one below:

{% tabs %}
{% tab title="Liquid formula for title" %}

<figure><img src="/files/zoxByEzQTbFsCcodgfvB" alt="The title field of Stockeo Data Mapping section with the Liquid formula"><figcaption><p>The <strong>title</strong> field of the Data Mapping section with the Liquid formula</p></figcaption></figure>
{% endtab %}

{% tab title="Copy the formula" %}

```
{{ Weight }} {{ Shape | downcase }} {{ "cut" }} {{ Color }} {{ Diamond_Type }}
```

{% endtab %}
{% endtabs %}

Will give the following result:

<figure><img src="/files/4lnTUYhs7CJz92xlbv45" alt="Shopify product title created with Stockeo automatically"><figcaption><p>Shopify product title created with Stockeo</p></figcaption></figure>

{% hint style="info" %}
The example above illustrates the creation of automatic product titles in Shopify using JSON as an example. Similarly, this works when the feed is a CSV or XML file. In that case, use the **column**/**node** name instead of the **field** name between the curly brackets in the Liquid formula.
{% endhint %}

### Translations

You can import product titles in different languages. Click on the gear icon to specify the desired language.

<figure><img src="/files/OCx876zboWdf4IjCVTMd" alt=""><figcaption></figcaption></figure>


# Description

How to create a product description in the Shopify store automatically based on the data from your supplier feed.

### Basic description settings

To pull a description from a specific node, simply indicate the node name from which you want Stockeo to extract a description. For example, if the text you want to use is included in the **\<description>** node, enter its name in the **Description** field of your automation as follows:

<figure><img src="/files/72VL9dIVwkh9mBSe1zr6" alt="Stockeo: basic settings for the description field"><figcaption><p>Stockeo: basic settings for the description field</p></figcaption></figure>

The node data may contain HTML and CSS text formatting information. The description will be formatted according to these.

### Create a description by splicing information from several nodes

It may be that the supplier feed contains a description, but you also want to include additional product information from other feed nodes.

```xml
<item>
    <id>40</id>
    <model>PF077</model>
    <name>Leather wallet for man with zip-pocket for coins</name>
    <option>Colour Tan</option>
    <description>
        <p class="p1"><span style="font-size:18px;"><span class="s1">Mens wallet made in genuine calfskin soft leather
        with zip-pocket for coins, 8 credit card holders and 1 document holder.</span></span></p>
        <p class="p1"><span style="font-size:18px;"><span class="s1">Simple lines and functional design,
        a perfect accessory to always have at hand money and documents.</span></span></p>
    </description>
    <attribute_group>
        <group_name>DETAILED FEATURES</group_name>
        <attribute>Length: 11 cm 4.2 in</attribute>
        <attribute>Height: 9 cm 3.5 in</attribute>
        <attribute>Credit card slots: 8</attribute>
        <attribute>Compartments for bills: 2</attribute>
        <attribute>Pocket for coins: 1</attribute>
        <attribute>Transparent window: 3</attribute>
        <attribute>Slot for business cards: 2</attribute>
    </attribute_group>
    <image_link>https://www.florenceleathermarket.com/image/data/prodotti/portafoglio-077/naturale/077CUO51.jpg</image_link>
    <image_galleries>
        <image_link>https://www.florenceleathermarket.com/image/data/prodotti/portafoglio-077/naturale/077CUO31.jpg</image_link>
    </image_galleries>
    <currency>USD</currency>
    <price_retail_gross>47.53</price_retail_gross>
    <quantity>3</quantity>
</item>
```

To include listed information from **attribute\_group** in the product description in your Shopify store you can use the Liquid formula in the **Description** field of the Stockeo automation.

Use the following settings:

{% tabs %}
{% tab title="Stockeo Description field" %}

<figure><img src="/files/MUBZpkQQTIaBNAGtZiRz" alt="Description settings in Stockeo automation for created description automatically"><figcaption><p>Description settings in Stockeo automation for created description automatically</p></figcaption></figure>
{% endtab %}

{% tab title="Copy settings" %}

```liquid
{{ description }}
{{ attribute_group.group_name }} {{"<br>"}}
{{ attribute_group.attribute[1] }} {{"<br>"}}
{{ attribute_group.attribute[2] }} {{"<br>"}}
{{ attribute_group.attribute[3] }} {{"<br>"}}
{{ attribute_group.attribute[4] }} {{"<br>"}}
{{ attribute_group.attribute[5] }} {{"<br>"}}
{{ attribute_group.attribute[6] }}
```

{% endtab %}
{% endtabs %}

To get the result as below:

<figure><img src="/files/ZtEt9UhrLIehm2ayst1o" alt="Automatically created description for a product in the Shopify store"><figcaption><p>Automatically created description for a product in the Shopify store</p></figcaption></figure>

{% hint style="info" %}
The example above illustrates the creation of automatic product descriptions in Shopify using XML as an example. Similarly, this works when the feed is a CSV or JSON file. In that case, use the **column**/**field** name instead of the **node** name between the curly brackets in the Liquid formula.
{% endhint %}

### Translations

You can import product descriptions in different languages. Click on the gear icon to specify the desired language.

<figure><img src="/files/4Voo7cqAekzTXYLnkrp2" alt=""><figcaption></figcaption></figure>


# Image URL settings

### XML

You may encounter an XML structure where links to images are stored in separate **Image** nodes.

{% tabs %}
{% tab title="Screenshot" %}

<figure><img src="/files/Qcx4hJvQF7hKTg3ky4Bi" alt="Images stored in separate Image nodes annotated"><figcaption><p>Images stored in separate Image nodes</p></figcaption></figure>
{% endtab %}

{% tab title="XML snippet" %}

```xml
<Product>
  <Items>
    <ProductItem>
      <ItemCode>A012345</ItemCode>
      <ItemImage1>http://warehouse.com/images/A012345.jpg</ItemImage1>
      <ItemImage2>http://warehouse.com/images/A012345_1.jpg</ItemImage2>
    </ProductItem>
  </Items>
</Product>
```

{% endtab %}
{% endtabs %}

For the notation above, use the following settings in the Image URL fields:

{% tabs %}
{% tab title="Screenshot" %}

<figure><img src="/files/5DELI6mR6zCbYx5sKY1W" alt="Stockeo settings for Image URL fields when images are stored in separate ItemImage nodes annotated"><figcaption><p>Stockeo settings for Image URL fields when images are stored in separate ItemImage nodes</p></figcaption></figure>
{% endtab %}

{% tab title="Copy from here" %}
**Image URL:** Items/ProductItem/ItemImage1

**Image URL:** Items/ProductItem/ItemImage2
{% endtab %}
{% endtabs %}

Another case is when the URLs are nested as the same named children of the Image node.

{% tabs %}
{% tab title="Screenshot" %}

<figure><img src="/files/g7tCKd3i3euwBg9WDAru" alt="URLs are nested as the same named children of the Image node annotated"><figcaption><p>URLs nested as the same named children of the Image node</p></figcaption></figure>
{% endtab %}

{% tab title="XML snippet" %}

```xml
<Product>
  <Items>
    <ProductItem>
      <ItemCode>A012345</ItemCode>
      <ItemImages>
        <ItemImage>http://warehouse.com/images/A012345.jpg</ItemImage>
        <ItemImage>http://warehouse.com/images/A012345_1.jpg</ItemImage>
      </ItemImages>
    </ProductItem>
  </Items>
</Product>
```

{% endtab %}
{% endtabs %}

You can extract them with the following settings:

{% tabs %}
{% tab title="Screenshot" %}

<figure><img src="/files/j02RImrgH94qcWuAZ8Pq" alt="Stockeo settings when URLs are nested as the same named children of the Image node annotated"><figcaption><p>Stockeo settings when URLs are nested as the same named children of the Image node</p></figcaption></figure>
{% endtab %}

{% tab title="Copy from here" %}
**Image URL:** Items/ProductItem/ItemImages/ItemImage\[1]

**Image URL:** Items/ProductItem/ItemImages/ItemImage\[2]
{% endtab %}
{% endtabs %}

Please note that the indexing of XML lists starts with 1.

### JSON

In the JSON file images may be stored as an array of objects that consist of e.g. image id and image URL.

{% tabs %}
{% tab title="Screenshot" %}

<figure><img src="/files/0Va9VslOYdG2JoKZ1Ntg" alt="Image URLs are stored as a property of the object in the product_images array annotated"><figcaption><p>Image URLs are stored as a property of the object in the product_images array</p></figcaption></figure>
{% endtab %}

{% tab title="JSON snippet" %}

```json
{
  "data": [
    {
      "product_code": "A012345",
      "product_name": "Sitting Jazz Band Strings",
      "product_images": [
        {
          "id": 5856,
          "image": "https://warehouse.com/images/A012345.jpeg"
        },
        {
          "id": 5857,
          "image": "https://warehouse.com/images/A012345_1.jpeg"
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

In this case use the following settings:

{% tabs %}
{% tab title="Screenshot" %}

<figure><img src="/files/09Fom5ealMYKquJUFtRc" alt="Stockeo settings when Image URLs are stored as a property of the object in the product_images array annotated"><figcaption><p>Stockeo settings when Image URLs are stored as a property of the object in the product_images array</p></figcaption></figure>
{% endtab %}

{% tab title="Copy from here" %}
**Image URL:** product\_images\[0].image

**Image URL:** product\_images\[1].image

**Image URL:** product\_images\[2].image
{% endtab %}
{% endtabs %}

Please note that the indexing of array elements in JSON starts at 0.

You can add fields up to the highest expected number of images. This will not cause an error, even if there is no link to the image on the given index.

### Multiple image URLs in a single field/node/column

In some feeds, multiple image URLs may be stored in a single field, node, or column.

<figure><img src="/files/hCwR6lKnUZM1ytHxiZeI" alt="Example CSV feed showing multiple image URLs combined in one column separated by delimiters"><figcaption><p>Sample feed with multiple image URLs stored in a single column</p></figcaption></figure>

In such cases, use a Liquid formula together with multiple Image URL fields.

1. Use a Liquid formula to split the value into an array of URLs. Apply the `split` filter and specify a separator (in this case, a semicolon). Repeat this formula in each Image URL field in the Data Mapping section.
2. In each subsequent Image URL field, reference the next element from the array. Note that indexing starts from 0, regardless of the feed format.

```liquid
{%- assign image_urls = Images | split: ";" %}{{ image_urls[0] }}
```

{% tabs %}
{% tab title="Screenshot" %}

<figure><img src="/files/9Lg3Sii8ciQY0HDsUl3o" alt="Configuration screen showing Image URL fields using a split formula to handle multiple URLs from one node"><figcaption><p>Image URL settings for extracting multiple URLs from a single node</p></figcaption></figure>
{% endtab %}

{% tab title="Copy from here" %}
**Image URL:**

```liquid
{%- assign image_urls = Images | split: ";" %}{{ image_urls[0] }}
```

**Image URL:**

```liquid
{%- assign image_urls = Images | split: ";" %}{{ image_urls[1] }}
```

**Image URL:**

```liquid
{%- assign image_urls = Images | split: ";" %}{{ image_urls[2] }}
```

**Image URL:**

```liquid
{%- assign image_urls = Images | split: ";" %}{{ image_urls[3] }}
```

**Image URL:**

```liquid
{%- assign image_urls = Images | split: ";" %}{{ image_urls[4] }}
```

{% endtab %}
{% endtabs %}


# Status

Stockeo allows you to define the status of products processed by an automation. It supports all product statuses available in the current Shopify API version:

* **Active**
* **Draft**
* **Archived**
* **Unlisted**

You can decide whether to set the same status for all processed products using a fixed value, or to retrieve the status from a specific field in the feed.

<figure><img src="/files/gqXnaDDd9EddceZdVUsx" alt="Screenshot showing the fixed status settings for products in Stockeo"><figcaption><p>Fixed status settings in Stockeo</p></figcaption></figure>

<figure><img src="/files/cOSS9P3xRohQodbY3psW" alt="Screenshot showing how Stockeo maps product status values from the feed to Shopify"><figcaption><p>Stockeo mapping product status from the feed</p></figcaption></figure>

If the status values in the feed differ from those supported by Shopify, you can use a **Liquid formula** to convert them to valid Shopify statuses.

Assume the feed uses the value **`discontinued`** for products that should be archived in Shopify.

<figure><img src="/files/FdxcC4TJixQbJhaewKbX" alt="Screenshot showing example product status values as provided in the feed"><figcaption><p>Sample product statuses from the feed</p></figcaption></figure>

In this case, you can apply the following Liquid formula to convert the feed value to a Shopify-supported status:

```liquid
{{ ['Product Status'] | replace: "discontinued","archived" }}
```

<figure><img src="/files/sAYi8FQdTZOdyWmmSAa2" alt="Screenshot showing a Liquid formula in Stockeo used to convert product status values from the feed to Shopify-supported statuses"><figcaption><p>Stockeo Liquid formula to convert product status</p></figcaption></figure>


# Sales Channel

Stockeo allows you to publish products to selected sales channels. To do this, add a **Sales Channel** field to the **Data Mapping** section for each sales channel where the product should be published.

<figure><img src="/files/mzPgNyon0lAQEvAbYgRd" alt="Screenshot showing Stockeo settings for publishing products to selected sales channels"><figcaption><p>Publishing products to selected sales channels in Stockeo</p></figcaption></figure>

Please note that products must have an **Active** [status](/data-mapping/status) to be published to any sales channel.

The app can only publish products to the selected sales channels. Removing a sales channel field from the **Data Mapping** section will not unpublish the product from that sales channel.


# Shopify Markets

**Shopify Markets** is a feature that helps merchants manage international sales from a single store by configuring currencies, languages, pricing, and domains for different regions.

**Shopify Catalogs** allow merchants to control which products and prices are available to specific markets, customers, or sales channels, enabling flexible product and pricing strategies.

Together, **Shopify Markets** define *where* and *to whom* products are sold, while **Shopify Catalogs** define *what* products and prices are offered within each market, allowing precise control over international storefronts.

Stockeo allows you to update product prices in specific catalogs based on pricing from the feed.

<figure><img src="/files/hTemrCJw2y8B0OvyzBGK" alt="Example of a CSV feed containing separate price columns for different Shopify markets"><figcaption><p>Sample CSV feed showing different prices assigned to multiple markets</p></figcaption></figure>

Add the **Market Price** field to the **Data Mapping** section. Select the catalog for which you want to update prices, then enter the field, node, or column from the feed that contains the corresponding price.

<figure><img src="/files/fMaa8mJbEHZxkzwLsGJk" alt="Animation showing how to configure data mapping for different Shopify Markets catalogs"><figcaption><p>Example of setting up price mapping for Shopify Markets catalogs</p></figcaption></figure>

If you want to add a markup to prices imported for individual catalogs, you can apply a Liquid formula directly in the **Price** field.

<figure><img src="/files/2qMxQGJADWoNc8VMvvRt" alt="Market Price field in Stockeo Data Mapping showing a Liquid formula used to add a markup to prices for a specific Shopify Markets catalog"><figcaption><p>Applying a Liquid formula to add a price markup for a Shopify Markets catalog</p></figcaption></figure>


# Translations

Stockeo makes it possible to import product titles and descriptions in different languages.

To configure translations, click on the gear icon next to the Title or Description field.

The first time, you'll need to approve the permission to let Stockeo access your store languages, and modify translations.

<figure><img src="/files/R793SbDh1rIlrPCcC8Z5" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/MsCzsQig47hV90OaRiQZ" alt=""><figcaption></figcaption></figure>

To set product translations, you also need to [add the desired languages in your store settings](https://help.shopify.com/en/manual/international/languages/manage-languages#add-new-language).

<figure><img src="/files/JpLE4mnvuODWH0cq0fiy" alt=""><figcaption></figcaption></figure>

Once the new permission is granted, and languages are added, you can indicate the fields where titles and descriptions are translated to a specific language in the feed.

For example, assuming that the feed is in JSON format with the following structure:

```javascript
[{
    "sku": "PROD001",
    "title": {
        "en": "Wireless Headphones",
        "de": "Kabellose Kopfhörer",
        "es": "Auriculares Inalámbricos"
    },
    "description": {
        "en": "High-quality wireless headphones with noise-cancellation."
        "de": "Hochwertige kabellose Kopfhörer mit Geräuschunterdrückung."
        "es": "Auriculares inalámbricos de alta calidad con cancelación de ruido."
    }
    "quantity": 150
}, {
   ...
}]
```

The data mapping to update titles and descriptions in English, German, and Spanish should look like this:

<figure><img src="/files/LI7CpkdEh6njrjeMMk7b" alt=""><figcaption><p>Data Mapping - multi-language translations</p></figcaption></figure>


# Continue selling when out of stock

Stockeo offers a possibility to import the inventory policy for individual products and variants depending on the input feed.

In the Data Mapping section, open the Add field dropdown, and select "Continue selling when out of stock". Then specify the field in your product feed that determines whether out-of-stock purchases are allowed for particular items.

<figure><img src="/files/OeyWmE7mdEVlnK0uSQs8" alt=""><figcaption></figcaption></figure>

The "Continue selling when out of stock" field accepts the following values.

Positive values:

* continue, Continue, CONTINUE
* yes, Yes, YES
* true, True, TRUE
* 1

Negative values:

* deny, Deny, DENY
* no, No, NO
* false, False, FALSE
* 0

You can use a Liquid expression to convert unsupported values, or to specify a fixed value.

For example, when your feed contains a backorder field with one of the two values *allow*, and *disallow*:

```
{%- case backorder -%}
{%- when "allow" -%} continue
{%- when "disallow" -%} deny
{%- else -%} deny
{%- endcase -%}
```

To specify a fixed value:

```
{{ "yes" }}
```


# Charge tax

Stockeo allows you to decide whether to charge tax on a product based on data from the product feed.

In the Data Mapping section, open the Add field dropdown, and select "Charge tax". Then specify the field in your product feed that determines whether a product is taxable.

<figure><img src="/files/k1ctbjMsxtSc2mxr0ALu" alt=""><figcaption><p>Stockeo - <em>Charge tax</em> field</p></figcaption></figure>

The "Charge tax" field accepts the below values.

Positive values:

* yes, Yes, YES
* true, True, TRUE
* 1

Negative values:

* no, No, NO
* false, False, FALSE
* 0

You can use a Liquid expression to convert unsupported values, or to specify a fixed value.

For example, when your feed contains a VAT\_Taxable field with one of the two values *Y*, and *N*:

```
{%- case VAT_Taxable -%}
{%- when "Y" -%} yes
{%- when "N" -%} no
{%- else -%} yes
{%- endcase -%}
```

To specify a fixed value:

```
{{ "yes" }}
```


# Feed Source

Stockeo can retrieve or receive the feed via the following channels:

* [HTTP(S)](/settings/feed-source/http-s)
* [FTP(S)](/settings/feed-source/ftp-s)
* Email
* Manual Upload


# HTTP(S)

Stockeo can fetch product feeds from any HTTP endpoint — a supplier's REST API, a SOAP service, or a plain feed URL. You provide the URL, choose a request method, and optionally configure authentication and custom headers.

### Request Settings <a href="#request-settings" id="request-settings"></a>

<figure><img src="/files/UJAod2yPAffflMRa1JrI" alt="Sample configuration screen showing HTTP feed source settings with a product feed URL"><figcaption><p>Example HTTP channel settings for a feed source</p></figcaption></figure>

#### Method <a href="#method" id="method"></a>

Choose **GET** or **POST**. Most APIs and feed URLs use GET. Select POST when the endpoint expects a request body (for example, a SOAP service or a JSON API that requires query parameters in the body).

#### URL <a href="#url" id="url"></a>

The endpoint Stockeo will call to retrieve a product feed. The URL can contain **variables** enclosed in double curly braces — these are replaced with actual values before the request is made.

**Pagination** — If your supplier's API returns results across multiple pages, include the `{{ page }}` variable in the URL. Stockeo will start at page 1, increment the page number after each request, and keep fetching until the response stops changing or 100 pages are reached.

{% hint style="info" %}
The `{{ page }}` variable supports Liquid filters. For example, if the API uses zero-based page numbering, use `{{ page | minus: 1 }}`.
{% endhint %}

#### Body (POST only) <a href="#body-post-only" id="body-post-only"></a>

When the method is set to POST, a **Body** field appears where you can enter the request payload. This is typically JSON or XML, depending on what the endpoint expects.

#### Headers <a href="#headers" id="headers"></a>

You can add custom HTTP headers to the request. Each header consists of a **key** and a **value**. Click **Add header** to add more. Headers are useful for setting content types, API keys, or any other custom values required by the endpoint.

### Authentication <a href="#authentication" id="authentication"></a>

Stockeo supports four authentication methods:

#### No Auth <a href="#no-auth" id="no-auth"></a>

No authentication is sent with the request. Use this for public endpoints or when authentication is handled in another way — for example, through a custom header or an API key passed directly in the URL as a query parameter (e.g. `https://supplier.com/api/inventory?api_key=YOUR_KEY`).

#### Basic Auth <a href="#basic-auth" id="basic-auth"></a>

Standard HTTP basic authentication. Enter a **username** and **password**, and Stockeo will send them as an `Authorization: Basic ...` header with each request.

#### Bearer Token <a href="#bearer-token" id="bearer-token"></a>

Token-based authentication. Enter the **token** value, and Stockeo will include it as an `Authorization: Bearer ...` header.

#### Digest

A more secure alternative to Basic authentication. Enter a username and password, and Stockeo will handle the challenge–response flow required by the server. Credentials are not sent in plain text; instead, a hash-based response is computed using the username, password, and server-provided parameters (such as nonce and realm), and included in the `Authorization: Digest ...` header.

#### Request <a href="#request" id="request"></a>

Use this when the API requires a separate authentication step before the main request — for example, when you need to call a login endpoint first to obtain a temporary token.

Configure the authentication request:

1. Choose the **Method** (GET or POST).
2. Enter the **Auth URL** — the endpoint that returns the authentication token.
3. For POST requests, select the **Content Type** and enter the **Body** (e.g. JSON with your API credentials).

After saving, Stockeo will first call the Auth URL, then use the response in the main request. Reference the authentication response in the main request URL or headers using the `{{ auth }}` variable. If the authentication response is JSON, you can reference specific fields — for example, `{{ auth.token }}` or `{{ auth.access_token }}`.

**OAuth2 (Request method)**

OAuth 2.0 is a standard for delegated authorization that allows applications to obtain limited access to an API on behalf of a user or system.

In Stockeo, OAuth2 is handled as a special case of the **Request** authentication method, because it requires an initial request to retrieve an access token before calling the main endpoint.

In a typical setup:

* A **POST** request is sent to the token endpoint (Auth URL)
* The request includes credentials (e.g. `client_id`, `client_secret`)
* The response contains an **access token**

This token is then used in the main request, typically in the Authorization header.

**Example (Client Credentials flow)**

Auth request:

```
POST https://api.supplier.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=

Body:
grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET
```

Response:

```
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}
```

Authorization header used in the main request:

```
Authorization: Bearer {{ auth.access_token }}
```

Setup:

<figure><img src="/files/0jUI9iv88MAOvC6iM7B4" alt="Configuration screen showing OAuth2 authentication settings in Stockeo"><figcaption><p>OAuth2 authentication setup in Stockeo</p></figcaption></figure>

Depending on the specific API, this flow may vary slightly (e.g. different parameters, request format, or authentication method). Always refer to the API documentation for the exact implementation details.

### Testing <a href="#testing" id="testing"></a>

Click the **Test** button to send a test request and see the response. This helps verify that the URL, authentication, and headers are configured correctly before saving. The response status code and body are displayed below the button.

<figure><img src="/files/u2P2bl1C5VBRQRSPg7tS" alt="Interface showing a test HTTP request and its response in Stockeo"><figcaption><p>Example of an HTTP request and response test in Stockeo</p></figcaption></figure>


# FTP(S)

Stockeo can connect to an FTP server to automatically retrieve feeds from your supplier. Three protocols are supported: FTP, FTPS, and SFTP.

## Connection Setup <a href="#connection-setup" id="connection-setup"></a>

<figure><img src="/files/zMVg3XQOHPaugQKkZh9j" alt="Configuration screen showing Stockeo feed source settings for an FTP channel"><figcaption><p>Stockeo feed source configuration for FTP channel</p></figcaption></figure>

#### Protocol <a href="#protocol" id="protocol"></a>

Choose `ftp://`, `ftps://`, or `sftp://`. Changing the protocol automatically updates the default port.

<table><thead><tr><th width="105">Protocol</th><th width="548">Description</th><th>Default port</th></tr></thead><tbody><tr><td><strong>ftp://</strong></td><td>Standard, unencrypted FTP. Credentials and file data are transmitted in plain text.</td><td>21</td></tr><tr><td><strong>ftps://</strong></td><td>FTP over TLS (encrypted). Stockeo handles both explicit and implicit TLS automatically. If your server uses a self-signed certificate, enable the A<strong>ccept self-signed certificate</strong> option.</td><td>990</td></tr><tr><td>sftp://</td><td>SFTP over SSH — a completely separate protocol from FTP/FTPS. Supports <strong>password</strong> and <strong>private key</strong> authentication.</td><td>22</td></tr></tbody></table>

#### Host <a href="#host" id="host"></a>

Hostname or IP address of the server (e.g. `files.example.com`).

#### Port <a href="#port" id="port"></a>

Port number. Defaults: `21` for FTP, `990` for FTPS, `22` for SFTP.

#### Accept self-signed certificate <a href="#accept-self-signed-certificate" id="accept-self-signed-certificate"></a>

FTPS only. Enable this option if the server uses a self-signed TLS certificate.

#### Username <a href="#username" id="username"></a>

Your FTP/SFTP login name.

#### Password <a href="#password" id="password"></a>

Password for FTP, FTPS, and SFTP with password authentication.

#### Private Key <a href="#private-key" id="private-key"></a>

SFTP with private key authentication only. Paste the full contents of your OpenSSH private key (beginning with `-----BEGIN OPENSSH PRIVATE KEY-----`). The server must have the corresponding public key in its `authorized_keys` file.

#### File path <a href="#file-path" id="file-path"></a>

The **File path** field tells Stockeo where to look for product feed. It supports three formats, each producing different processing behaviour.

**Static path**

A fixed path to a single file.

```
/inventory/stock.csv
```

Stockeo checks whether the file has been **modified** since the last run. If the file has not changed, it is skipped and the run is recorded as `File has not changed since last run`. If it has changed, the file is downloaded and processed.

**Wildcard path**

A path with `*` or `?` wildcards in the filename.

```
/inventory/stock_*.csv
```

**Liquid path**

A path containing Liquid expressions that are evaluated before the file lookup.

```
/inventory/stock_{{ "now" | moment: "YYYY-MM-DD", "America/Chicago" }}.csv
```

The Liquid expression is resolved to a concrete filename using the current date and time. This makes it possible to dynamically target files whose names contain timestamps, dates, or other calculated values.

For example, the path above would be resolved to:

```
/inventory/stock_2026-06-08.csv
```

In this case, you can make use of the Stockeo custom Liquid filter [moment](/custom-liquid-filters).

### Firewall Whitelist <a href="#firewall-whitelist" id="firewall-whitelist"></a>

Stockeo connects to your FTP server from a fixed IP address. If your supplier's server is protected by a firewall, you or your supplier will need to whitelist the following IP:

```
138.197.226.212
```

Pass this address to whoever manages the FTP server so they can allow incoming connections from it.

### Connecting and Testing <a href="#connecting-and-testing" id="connecting-and-testing"></a>

After filling in all fields, click **Connect**. Stockeo will establish a connection to the server and save the credentials.

If the file is not found at the given path, the connection still succeeds but a warning is shown next to the **File path** field. You can proceed with saving and Stockeo will look for the file again at the next scheduled run.

Once connected, the **Edit** button appears and all fields become read-only. Clicking **Edit** unlocks the fields so you can update the connection details.


# Filters

Setting filters allows filtering data based on whether it meets particular condition. You can add and configure new filters by clicking on the **Add filter** button in the Filters section. Available filtering options are filtering by vendor, feed data or product tag.

<figure><img src="/files/kVlm8IsCT9q1Mu3lpQt9" alt=""><figcaption><p>Add filter button in the Filters section</p></figcaption></figure>

### Feed Data

Filtering by feed data allows extracting only that data from the feed, where a feed field matches given condition.

To filter by feed data you need to select **Feed Data** in the Field dropdown.

<figure><img src="/files/4wEwYItOgjt9OMyLfCFW" alt=""><figcaption><p>Filtering by feed data</p></figcaption></figure>

**Filtering conditions**

To showcase different filtering conditions, assume you're importing a CSV file with the following columns:

* sku
* name
* quantity
* brand

<table data-full-width="false"><thead><tr><th>sku</th><th width="178">name</th><th>quantity</th><th>brand</th></tr></thead><tbody><tr><td>XYZ001</td><td>T-shirt</td><td>25</td><td>SPORTWEAR</td></tr><tr><td>XYZ002</td><td>T-shirt</td><td>0</td><td>SPORTWEAR</td></tr><tr><td>XYZ003</td><td>T-shirt</td><td>25</td><td>FITCLOTHES</td></tr><tr><td>XYZ004</td><td>T-shirt</td><td>25</td><td>FINECLAD</td></tr><tr><td>XYZ005</td><td>Leggings</td><td>25</td><td>SPORTWEAR</td></tr><tr><td>XYZ006</td><td>Leggings</td><td>25</td><td>SPORTWEAR</td></tr><tr><td>XYZ007</td><td>Leggings</td><td>0</td><td>FITCLOTHES</td></tr><tr><td>XYZ008</td><td>Leggings</td><td>25</td><td>FINECLAD</td></tr></tbody></table>

#### IS condition

Assume you want to import only those items where brand equals SPORTWEAR. To do that you need to specify column name of which data will be checked and choose the condition that the data must meet to be imported. In this case the according filtering condition is the **is** condition.

<figure><img src="/files/tuDEX0hBQve7VbMLZ5nN" alt=""><figcaption><p>Setting filter to import only those items where brand equals SPORTWEAR</p></figcaption></figure>

#### IS NOT condition

Now assume you want to import only those items where brand is not SPORTWEAR. To do that, you need to use the **is not** condition.

<figure><img src="/files/rRgQwtlnvXieY23s2PIA" alt=""><figcaption><p>Setting filter to import only those items where brand is not equal to SPORTWEAR</p></figcaption></figure>

#### CONTAINS condition

Another filtering condition is the **contains** condition. Assume you want to import all those items where brand is equal to either SPORTWEAR or FINECLAD. To set that filter, you need to choose the **contains** condition, and set the **Value** field to concrete column data values you accept, separated by commas. In this case the **Value** field is set to *SPORTWEAR, FINECLAD*.

<figure><img src="/files/dwkOPOeHycswsjLPY5Xu" alt=""><figcaption><p>Setting filter to import items where brand is SPORTWEAR or FINECLAD</p></figcaption></figure>

#### DOESN'T CONTAIN condition

Now assume you want to import the items of all brands except SPORTWEAR and FINECLAD. To do that you need to choose the **doesn't contain** condition.

<figure><img src="/files/00HvgnXG6cSAuun7009L" alt=""><figcaption><p>Setting filter to import items where brand is not SPORTWEAR and FINECLAD</p></figcaption></figure>

#### GREATER THAN condition

Assume you want to import items where quantity is greater than 0. In that case you need to use the **greater than** condition.

<figure><img src="/files/PPfMooM9MZf0Ho0BFuYk" alt=""><figcaption><p>Setting filter to import items where quantity is greater than 0</p></figcaption></figure>

#### Joining conditions

If you want to filter by multiple conditions you can do that by adding multiple filters. Assume you want to import those items where brand is equal to FITCLOTHES and quantity is greater than 0.

<figure><img src="/files/bb93Z3JDmG9F6RKKjOb3" alt=""><figcaption><p>Setting filter to import items where brand equals FITCLOTHES and quantity is greater than 0</p></figcaption></figure>

### Product Tag

Filtering by product tags is a solution for situations where products from different suppliers might have the same SKUs. Marking the items with a tag, and setting a filter by product tag secures the automation to modify correct items.

Filtering by product tags also allows for faster automation runs. When Stockeo runs the automation, it scans existing variants in the store. Adding filtering by product tag allows minimazing the amount of scanned items to those with a given tag, therefore shortening the duration of the automation.

{% hint style="info" %}
To filter by product tags, items in the store must have specified tags.
{% endhint %}

When filtering by product tag, there are two filtering conditions available: **contain** and **don't contain**.

#### Example

Assume you want to update items marked with FITCLOTHES tag. To do that you need to choose the **contain** condition.

<figure><img src="/files/EYUOSQtV2DTJbeg2LovP" alt=""><figcaption><p>Filtering by product tag</p></figcaption></figure>

If you want to filter by multiple tags, the **contain** condition is also applicable. You can set the **Value** field to multiple tags separated by commas. Automation will then scan only those variants that have either tag that was set in the **Value** field.

Using the **don't contain** condition will cause the automation to scan those variants that aren't marked with a given tag.

<figure><img src="/files/Jv2UScwNZ9BIBs7Zzy4V" alt=""><figcaption><p>Filtering items that don't have the FITCLOTHES tag</p></figcaption></figure>

### Vendor

Filtering by vendor has a similar purpose as filtering by product tags, in a case when products from different vendors might have the same SKUs, setting the vendor for each item and then filtering by vendor secures the automation to modify correct items.

When filtering by vendor, available filtering options are **is** and **is not** conditions.

{% hint style="info" %}
To filter by vendor, items in the store must have specified vendors.
{% endhint %}

#### Example

Assume you want to update those items where vendor is FITCLOTHES-VENDOR.

<figure><img src="/files/TsnEeH65j8GondoVmPb6" alt=""><figcaption><p>Setting filter to match items from FITCLOTHES-VENDOR</p></figcaption></figure>

If you want to modify items for every vendor except FITCLOTHES-VENDOR you can use the **is not** condition.

<figure><img src="/files/IqasG8dMhPkA8IzXyY3O" alt=""><figcaption><p>Setting filter to match all items except those from FITCLOTHES-VENDOR</p></figcaption></figure>


# Troubleshooting

<br>


# Duplicate products created during import

Stockeo includes built-in mechanisms to prevent duplicate product creation during imports by matching incoming products with existing items in your store. However, certain configuration choices, such as vendor/product tag filters or SKU prefixes, may affect the matching process and cause products to be imported as duplicates if not configured correctly.

#### Vendor and Product Tag Filters

Before importing products, Stockeo creates a snapshot of the products and variants in your Shopify store to determine which items should be updated and which should be created.

When vendor or product tag filters are configured, only products matching those filters are included in the snapshot. If a product from the supplier's feed already exists in your store but does not have the required vendor or product tag assigned, Stockeo will not recognize it as an existing product and will import it again, creating a duplicate.

To prevent duplicates:

* When using vendor or product tag filters, ensure that the automation is configured to assign the same vendor or product tag to newly imported products. Make sure the *Fixed value* option is enabled.

<figure><img src="/files/N2Em7gagdqK66AzE2hoq" alt="Stockeo showing correct configuration of fixed vendor settings and product filtering"><figcaption><p>Correct setup of fixed vendor settings and filtering in Stockeo to ensure proper product matching during import</p></figcaption></figure>

* If some products from the supplier already exist in your store, ensure that all of them have the vendor or product tag specified in the automation filters.

<figure><img src="/files/436eZdXmUZXa4dHkRM2J" alt="Shopify product settings showing the Product Vendor field"><figcaption><p>Product Vendor field in Shopify</p></figcaption></figure>

#### SKU Prefixes

If products from the supplier's feed already exist in your Shopify store and their SKUs match the SKUs in the feed, adding a SKU prefix in the automation can result in duplicate products being created.

When a SKU prefix is configured, Stockeo matches products only against SKUs that already include the prefix.

<figure><img src="/files/lxQYFfDPET53CZNPFrCD" alt="Stockeo automation settings showing the SKU prefix configuration"><figcaption><p>Configuring a SKU prefix in Stockeo to modify imported product SKUs</p></figcaption></figure>

Existing products without the prefix will not be recognized as matches, causing the same products to be imported again with the prefixed SKUs.

To prevent duplicates:

* Do not add a SKU prefix if matching products already exist in your store without that prefix.
* If a SKU prefix is required, ensure that the existing products use the same prefixed SKU format before running the import.


# Dates are not imported correctly from an Excel feed

Assume a date in Excel is displayed in the following format:

<figure><img src="/files/75RDlumNweSEjGuIqH3o" alt="Date displayed in an Excel cell"><figcaption><p>A date value displayed in an Excel cell</p></figcaption></figure>

However, the actual value passed to Shopify by Stockeo is `46183`. This is because Excel stores dates as serial numbers. In this example, the value represents the number of days since `1900-01-01`, which corresponds to the date `2026-06-10`.

To convert the value into a readable date format, a Liquid formula must be applied. Since Liquid supports Unix timestamps, which are based on `1970-01-01`, the formula first subtracts the number of days between `1900-01-01` and `1970-01-01` (`25569`) from the Excel date value. It then multiplies the result by the number of seconds in a day (`86400`) to obtain a Unix timestamp and finally formats the timestamp using the Liquid `date` filter.

If the date is stored in column **B**, the formula should be:

```liquid
{{ B | minus: 25569 | times: 86400 | date: "%Y-%m-%d" }}
```

<figure><img src="/files/XPBlzXTZe22HHcl58ZGw" alt="Liquid formula configured in the Stockeo Data Mapping section to convert an Excel serial date into a formatted date value"><figcaption><p>Liquid formula configured in the Stockeo Data Mapping section to convert an Excel serial date into a formatted date value</p></figcaption></figure>

This formula converts the Excel serial date into a Unix timestamp and outputs it in the format specified by the Liquid `date` filter.


# Custom Liquid filters

Filters are simple methods that modify the output of numbers, strings, variables, and objects. They are placed within an output tag `{{ }}` and are denoted by a pipe character `|`. In addition to the standard filters, Stockeo also provides custom filters.

#### base64\_encode <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

Encodes the provided input into [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64). If the input is binary data, it is directly converted into a Base64 string; otherwise, the input is first converted into a UTF-8 byte representation before encoding.

{% tabs %}
{% tab title="Input" %}

```liquid
{{ "Hello World!" | base64_encode }}
```

{% endtab %}

{% tab title="Output" %}

```
SGVsbG8gV29ybGQh
```

{% endtab %}
{% endtabs %}

#### base64\_decode <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

Decodes a Base64-encoded string back into its original form. The decoded binary data is then interpreted as UTF-8 text and returned as a readable string.

{% tabs %}
{% tab title="Input" %}

```liquid
{{ "SGVsbG8gV29ybGQh" | base64_decode }}
```

{% endtab %}

{% tab title="Output" %}

```
Hello World!
```

{% endtab %}
{% endtabs %}

#### ensure\_array <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

Ensures that the value is always returned as an array. If the input value is `null` or `undefined`, an empty array is returned. If the value is already an array, it is returned unchanged. Otherwise, the value is wrapped in a single-element array.

Assume that the node `attributes` may contain either one or more child nodes.

When parsing a node with a single child node, the result is a single object:

{% tabs %}
{% tab title="XML" %}

```xml
<attributes>
    <attribute>
        <name>Color</name>
        <value>Black</value>
    </attribute>
</attributes>
```

{% endtab %}

{% tab title="Parsing result" %}

```json
{
    "attribute": {
        "name": "Color",
        "value": "Black"
    }
}
```

{% endtab %}
{% endtabs %}

When parsing a node with multiple child nodes, the result is an array of objects:

{% tabs %}
{% tab title="XML" %}

```xml
<attributes>
    <attribute>
        <name>Switch Type</name>
        <value>Blue</value>
    </attribute>
    <attribute>
        <name>Layout</name>
        <value>US</value>
    </attribute>
    <attribute>
        <name>Backlight</name>
        <value>RGB</value>
    </attribute>
</attributes>
```

{% endtab %}

{% tab title="Parsing result" %}

```json
{
    "attribute": [
        {
            "name": "Switch Type",
            "value": "Blue"
        },
        {
            "name": "Layout",
            "value": "US"
        },
        {
            "name": "Backlight",
            "value": "RGB"
        }
    ]
}
```

{% endtab %}
{% endtabs %}

The `ensure_array` filter removes this ambiguity by ensuring a consistent array structure, making the Liquid formulas shorter and easier to maintain.

#### gzip <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

Compresses the provided input using the GZIP compression algorithm with a high compression level. It outputs the result as compressed binary data, which can optionally be encoded in Base64 using the [base64\_encode](#json_parse-parse_json) custom Liquid filter.

#### json\_parse, parse\_json <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

Allows parsing string in a format compliant with JSON file requirements into an object. It enables access to individual fields within the object using dot or bracket notation.

**Usage example:**\
\
Assume that individual product nodes don't contain category names, but only a `category_id`.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<shop>
    <categories>
        <category>
            <id>101</id>
            <name>Electronics</name>
        </category>
        <category>
            <id>102</id>
            <name>Clothing</name>
        </category>
        <category>
            <id>103</id>
            <name>Home and Garden</name>
        </category>
    </categories>
    <products>
        <product>
            <name>Wireless Headphones</name>
            <sku>ELEC-001</sku>
            <category_id>101</category_id>
        </product>
        <product>
            <name>Sports T-Shirt</name>
            <sku>SPORT-002</sku>
            <category_id>103</category_id>
        </product>
    </products>
</shop>
```

If you want to add categories as tags, you can create a formula that creates an object with key-value pairs, so-called dictionary variable. In this case, keys are categories ids, and values - their names. With the formula below, the category name will be found by its ID:

{% tabs %}
{% tab title="Liquid code" %}

```liquid
{%- capture categories -%}
{"101":"Electronics","102":"Clothing","103":"Home and Garden"}
{%- endcapture %}
{%- assign categories = categories | json_parse -%}
{{ categories[category_id] }}
```

{% endtab %}

{% tab title="Stockeo settings" %}

<figure><img src="/files/iLDxjd8D4JRIleirqpvn" alt="Configuration screen showing usage of the json_parse filter to map and process JSON data in Stockeo"><figcaption><p>Example settings using the <code>json_parse</code> filter in Stockeo</p></figcaption></figure>
{% endtab %}
{% endtabs %}

#### moment <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

Returns a current or specified time converted to a desired time zone.

The filter has two parameters: a date format and a [time zone identifier](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List).

{% tabs %}
{% tab title="Input" %}

```liquid
stock_{{ "now" | moment: "YYYY-MM-DD", "America/Chicago" }}.csv
```

{% endtab %}

{% tab title="Output" %}

```
stock_2026-06-02.csv
```

{% endtab %}
{% endtabs %}

| format | description                                            | example value |
| ------ | ------------------------------------------------------ | ------------- |
| DD     | Two-digit day of the month (with leading zeros)        | `01` to `31`  |
| MM     | Two-digit representation of the month                  | `01` to `12`  |
| YY     | Two-digit representation of the year                   | `26`          |
| YYYY   | Four-digit representation for the year                 | `2026`        |
| HH     | Two-digit representation of the hour in 24-hour format | `00` to `23`  |
| hh     | Two-digit representation of the hour in 12-hour format | `01` to `12`  |
| A      | Upper-case 'AM' or 'PM' based on the given time        | `AM` or `PM`  |
| a      | Lower-case 'am' or 'pm' based on the given time        | `am` or `pm`  |
| mm     | Two-digit representation of the minute                 | `00` to `59`  |
| ss     | Two-digit representation of the second                 | `00` to `59`  |

This filter can be used, for example, in the **File Path** field of an FTP channel to match files whose names contain date or time information, allowing the automation to process only feeds generated on the current day or during the current hour.

#### to\_fixed <a href="#to_fixed" id="to_fixed"></a>

Rounds the number to keep only the given number of decimals.

{% tabs %}
{% tab title="Input" %}

```liquid
{{ 1.2345 | to_fixed: 2 }}
```

{% endtab %}

{% tab title="Output" %}

```
1.23
```

{% endtab %}
{% endtabs %}

The difference between `round` and `to_fixed` it that `round` returns a number so it trims trailing zeros while `to_fixed` will preserve them.

#### xml\_unescape <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

Unescaping characters that are treated as special in XML documents:&#x20;

|                    |          |
| ------------------ | -------- |
| **Escaped String** | **Char** |
| \&lt;              | <        |
| \&gt;              | >        |
| \&quot;            | "        |
| \&apos;            | '        |
| \&amp;             | &        |

This is useful when special characters from HTML in the product description coming from the feed are escaped, causing the HTML syntax to be incorrectly interpreted by Shopify.

{% tabs %}
{% tab title="Input" %}

```html
&lt;p&gt;Exceptional coffee with vanilla &amp; chocolate flavor.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;100% Arabica&lt;/li&gt;
  &lt;li&gt;Freshly roasted&lt;/li&gt;
&lt;/ul&gt;
```

{% endtab %}

{% tab title="Output" %}

```html
<p>Exceptional coffee with vanilla & chocolate flavor.</p>
<ul>
  <li>100% Arabica</li>
  <li>Freshly roasted</li>
</ul>
```

{% endtab %}
{% endtabs %}

#### xml\_escape <a href="#json_parse-parse_json" id="json_parse-parse_json"></a>

It works in the opposite way to the `xml_unescape` filter. It replaces characters that are considered special in XML documents.

|          |                   |
| -------- | ----------------- |
| **Char** | **Escape String** |
| <        | \&lt;             |
| >        | \&gt;             |
| "        | \&quot;           |
| '        | \&apos;           |
| &        | \&amp;            |


# Invalid Closing Quote

How to fix the error Invalid Closing Quote: found non trimable byte after quote at line x?

{% hint style="info" %}
The error *Invalid Closing Quote: found non trimable byte after quote at line x* indicates that some quotes in the CSV feed aren't properly escaped.
{% endhint %}

Quotation marks in CSV files act as text qualifiers, wrapping text to keep it as one value instead of separating it into distinct values. This is crucial when importing CSV data into spreadsheet programs or databases, as it helps distinguish between commas that are delimiters and those that are part of the text.

**Correct:**

Quotes are properly escaped using double quotes ("") when included in text.

```csv
SKU,Product Name,RRP,Quantity
CLOTH001,"Men's ""Classic"" White T-Shirt",19.99,50
```

**Incorrect:**

```csv
SKU,Product Name,RRP,Quantity
CLOTH001,"Men's "Classic" White T-Shirt",19.99,50
```

```csv
SKU,Product Name,RRP,Quantity
CLOTH001,"Men's \"Classic\" White T-Shirt",19.99,50
```


# Invalid Record Length

How to fix the Invalid Record Length: expected x, got y on line z error?

{% hint style="info" %}
By default, Stockeo requires CSV files to have the same number of columns in every row. If the error message *Invalid Record Length: expected x, got y on line z* appears, it means that row z has a different number of columns than the previous rows.
{% endhint %}

Strict parsing of CSV files offers several advantages. For example, it helps prevent column misalignment when a product description contains a comma, but the value is not enclosed in quotes. This issue could lead to incorrect data mapping, such as mistakenly assigning the cost as the retail price if those fields are adjacent.

However, differences in the number of columns may still occur, often due to varying numbers of image URLs at the end of each row. The *Ignore inconsistent number of columns* checkbox lets you instruct the app to skip rows in the CSV file that don't match the column count of the header row. This is helpful when some rows have missing or additional data.

<figure><img src="/files/zun0IL16mAq247zwvAIe" alt=""><figcaption><p>Stockeo - Ingore inconsistent number of columns in CSV files</p></figcaption></figure>


# Signed Agent

Stockeo fetches product feeds only for merchants who explicitly configure a feed URL inside the app. It is not a general-purpose crawler and does not discover links on its own.

Stockeo identifies itself as a signed agent using Cloudflare Web Bot Auth. The signed requests are generated by Stockeo servers and use a dedicated Ed25519 signing key.

The public key directory used for request signing is available at [https://stockeo.solvenium.com/.well-known/http-message-signatures-directory](https://stockeo-dev.solvenium.com/.well-known/http-message-signatures-directory).


