Import Integrations

Overview

The Ka-ching system contains a number of flexible APIs for importing various types of data in various use cases.

Most of the examples shown here has been added to a Postman collection that you can download on the resources page.

The data types that can currently be imported are:

  • Products
  • Product Recommendations
  • Stock Counts
  • Discount Campaigns
  • Images
  • Product groups
  • Tags
  • Attributes
  • Folders
  • Tabs
  • Recommendation Categories

The common way to perform the import is through our https import endpoints. We recommend that data is pushed to the Ka-ching system on every data change in the integrating system. This will provide for real-time data updates in the entire chain. An alternative to this solution is to batch import updated products periodically, but that naturally eliminates the real-time aspect of the integration.

Some data can also be imported as CSV-files through our Back Office. This feature is only intended for a quick-start setup, since this requires manual steps, and the CSV format is not very well suited for hierarchically structured data like for instance our Products. This means that the flat CSV format cannot describe all available details for our products.

Finally data can be imported through actions performed in our clients. Please refer to #runtimexxx for documentation about these 'runtime' integrations.

The Product Import Endpoint

https://{BASE_URL}/imports/products

This endpoint uses Import Queues to handle all imported data. This strategy is detailed in Import Queues below, but basically it allows you to configure multiple import sources, and all traffic on each import source is logged in both success and error cases so that the import operations can be monitored and debugged.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/products?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Advanced Product Import Parameters

When importing Products you can additionally specify which Markets and Channels you wish to import for. The default market is dk (Denmark) and the default channel is pos - signifying the POS channel for all Shops in the Danish market.

These defaults can be overridden by the query parameters markets and channels respectively. The value for both parameters are comma separated lists of markets and channels.

E.g.:

https://{BASE_URL}/imports/products?markets=dk,se,no&channels=pos,online&...

For further clarification of the Market and Channel concepts, please read the Vocabulary.

Importing Products

Send a POST request to the endpoint with the parameters specified above.

The body of the POST request must be a JSON object with the following layout:

{
    "products": [
        [product A],
        [product B],
        ...
    ]
}

The definition of the Product model can be found in the Models document. The minimal data required for a regular product is an id, a retail_price and a name:

{
    "products": [
        {
            "id": "0001",
            "name": "Coffee",
            "retail_price": 25
        },
        {
            "id": "0002",
            "name": "Tea",
            "retail_price": 20
        }
    ]
}

NOTE

When products are imported into the Ka-ching system, the id will be used as a unique key.

One limitation in the database we are using (the Firebase real-time database) is that not all characters are valid to use in keys.

This means that the following list of characters may not be used in the "id" field of a product: ., /, #, $, [ and ]

Other basic properties of products include:

  • description
  • image_url
  • barcode

The description will be shown when displaying product details.

The image_url is a URL for an image that will be shown in the Product Grid, basket and Product Details.

The barcode could be either an EAN number, a SKU, an ISBN number or any other means of identifying a product. The only thing that matters for the POS is that this is the bar code that you would scan in the shop in order to add the product to the basket.

Advanced product models

Importing products with prices for different markets

All price information on a product will be interpreted to be in the currency of the market in which they are imported.

If you are importing for multiple markets, then you naturally need to be able to specify prices for each of these markets. This can be done by supplying the pricing information as objects keyed by the market. Here is an example of importing a few products in the markets: Denmark, Norway and Sweden:

{
    "products": [
        {
            "id": "0001",
            "name": "Coffee",
            "retail_price": {
                "dk": 25,
                "no": 35,
                "se": 30
            }
        },
        {
            "id": "000"",
            "name": "Tea",
            "retail_price": {
                "dk": 20,
                "no": 32,
                "se": 28
            }
        },
        ...
    ]
}

Localizing product names and other details

Throughout the Ka-ching system, data can be localized into multiple languages. This could for instance be used to display a chinese version of product names on a secondary, customer facing display.

The way this is modelled is that all string data can either be a simple, non-localized string -or- an object containing localizations for various language codes.

Localizing the product name could for instance be done as follows:

{
    "products": [
        {
            "id": "0001",
            "name": {
                "da": "Kaffe",
                "en": "Coffee",
                "nb": "Kaffe"
            },
            "retail_price": 25
        }
    ]
}

Variable priced products

The Ka-ching system allows for variable priced products. These are products that do not have an explicit retail price, but rather need the product price to be entered when the product is added to the basket on the POS.

Variable priced products can be created simply by not specifying a retail price.

{
    "products": [
        {
            "id": "0001",
            "name": "Variable priced coffee"
        }
    ]
}

Taxes

The market that your shop operates in defines a set of one or more default taxes. This means that in countries like Denmark, you never need to specify other taxes directly on the product.

But in special situations you have the option to override the set of taxes for individual products. This can be done as follows:

{
    "products": [
        {
            "id": "0001",
            "name": "Coffee",
            "retail_price": 25,
            "taxes": [
                {
                    "id": "coffee_tax",
                    "name": "Coffee tax",
                    "rate": 0.1,
                    "type": "vat"
                }
            ]
        }
    ]
}

Taxes can be either vat or sales_tax. This determines how the base price is calculated from the retail price. See the Vocabulary for more on this.

Notice that the taxes are specified as an array. This is because in some countries, multiple taxes can be in effect at the same time. For instance you could imagine taxation to be split into two rates defining 'city tax' and 'state tax'. Again the Vocabulary has more details on this concept.

Creating simple, fixed sale discounts

Our Discount Campaign engine can be used to model powerful discount concepts in a very expressive manner, but sometimes this complexity is unneeded.

So if a product is currently on sale, this sale_price can be added directly to the product.

When a product with a sale_price is added to the basket, a new price discount is automatically applied to the item.

{
    "products": [
        {
            "id": "0001",
            "name": "Coffee on sale",
            "retail_price": 25,
            "sale_price": 15
        }
    ]
}

Unlocking Contribution Ratio functionality in the POS

In the Ka-ching Back Office, you can configure a desired Contribution Ratio for your sales. In order for this to work, you need to supply a cost price for all products.

When a desired Contribution Ratio is configured, the Action Button in the bottom right of the POS will show a status indicating wether the Contribution Ratio is met (green) or not (orange) or if money are actually lost by performing the sale (red). This could be the case if too much of a discount is being applied to the sale.

Note that retail prices in Ka-ching always represent the price that you would print on a price-tag (meaning including VAT, but excluding american style sales tax), but cost prices are always represented without any taxes.

{
    "products": [
        {
            "id": "0001",
            "name": "T-shirt",
            "retail_price": 150,
            "cost_price": 40
        }
    ]
}

Purchase types

The concept of purchase types exists to let pricing and tax details of a product vary with an aspect of the sales situation. Most often this would be used to model varying prices and taxes for 'eat in' and 'dine out' sales. For instance taxation in many states in the US varies exactly based on this choice.

Currently the setup for the available purchase types needs to be performed by Ka-ching, but when the desired purchase types are added, you may specify price or tax variations for each purchase type in the manner shown below. Note that you can default to the base pricing and taxing, so if you are modelling 'eat in' vs. 'dine out', you only need to use once explicit purchase type.

For the example below, imagine that we have defined the purchase type 'dine_out', and that the absence of 'dine_out' means 'eating in'.

{
    "products": [
        {
            "id": "0001",
            "name": "Pie",
            "retail_price": 25,
            "retail_price_map": {
                "dine_out": 20
            }
        }
    ]
}

Similarly you can vary taxes by having both a 'taxes' and a 'taxes_map':

{
    "products": [
        {
            "id": "0001",
            "name": "Pie",
            "retail_price": 25,
            "taxes": [
                {
                    "id": "coffee_tax",
                    "name": "Coffee tax",
                    "rate": 0.1,
                    "type": "vat"
                }
            ],
            "taxes_map": {
                "dine_out": [
                    {
                        "id": "coffee_tax",
                        "name": "Coffee tax",
                        "rate": 0.07,
                        "type": "vat"
                    }
                ]
            }
        }
    ]
}

Product Groups

Product Groups can be defined in the Ka-ching Back Office and are used to group sold goods on the X- and Z-reports. A Product Group is defined as an identifier and a display name. By adding the product group identifier to a product on import, this will make the grouping appear on the reports.

A Product can belong to 1 or 0 Product Groups

{
    "products": [
        {
            "id": "0001",
            "name": "Pie",
            "retail_price": 25,
            "product_group": "deserts"
        }
    ]
}

More functionality may be added around the Product Group concept in the future.

Tags

Tags can be defined in the Ka-ching Back Office and are informal identifiers that can be added to a Product. A Tag is defined as an identifier and a display name.

In the Ka-ching POS, the tags can be used to create the tab bar and a folder hierarchy.

Tags can also be used in conjunction with Discount Campaigns as a means of triggering discounts in case a certain number of products bearing the same tag are in the shopping basket.

Any number og tags can be added to one Product.

{
    "products": [
        {
            "id": "0001",
            "name": "Pie",
            "retail_price": 25,
            "tags": {
                "deserts": true, 
                "food": true, 
                "popular": true
            }
        }
    ]
}

More functionality may be added around the Tag concept in the future.

Attributes

Attributes can be defined in the Ka-ching Back Office. They are similar to tags, but allow you to define stronger semantics between values.

Consider the tags merlot, cabernet, denmark and france. While all of these may be used for filtering purposes, there is nothing that binds merlot and cabernet to each other - and the same for denmark and france. One product could easily contain all four tags.

Instead you may wish to define a more formal relationship where you let the system know that merlot and cabernet are both types of grapes - and denmark and france are both countries.

From Ka-ching release 11 you may now specify an Attribute called Grape that can list all of the available grapes - and similarly for Country. For each of these two attributes, a product can only have a single value.

With these added semantics, each attribute can now be represented in Ka-ching POS as a search facet.

Any number og attributes can be added to one Product.

{
    "products": [
        {
            "id": "0001",
            "name": "2016, Grand Sud",
            "retail_price": 305,
            "attributes": {
                "grape": "merlot",
                "country": "france"
            }
        }
    ]
}

More functionality may be added around the Attribute concept in the future.

For instance, attributes can only be 'option sets' today. In the future they might also embrace numerical values and other data types.

Product Variants

In the Ka-ching system, a product can either be a stand-alone, sellable entity, but it can also be used to model the concept of Product Variants. If, for instance, a product can be sold in a big and a small variant, then this can be modelled as a product that has two variants. This basically means that the product itself is not a sellable entity (you cannot sell this product without knowing whether you are selling the big or the small variant), but each variant becomes a sellable entity instead.

The variant can have it's own name, barcode, image_url and can also specify price variations. If it does not specify a retail_price, the retail_price of the product will be used.

In the POS, tapping a product with variants will display the variant selector. Variants are displayed in a list unless dimensions are also specified (read more about dimensions below).

If all variants of a product have the same price, this price will be displayed in the Product Grid. If the prices of the variants vary, then the prices will only be shown when the product is tapped.

{
    "products": [
        {
            "id": "0001",
            "name": "Pie",
            "retail_price": 25,
            "variants": [
                {
                    "id": "small",
                    "name": "Small"
                },
                {
                    "id": "big",
                    "name": "Big",
                    "retail_price": 50
                }
            ]
        }
    ]
}

Dimensions and dimension values

Imagine the situation where you sell a shirt in sizes: S, M and L and colors: red, yellow and blue. This basically gives rise to 9 variants, so one way of modelling this would just be to include 9 variants with the names "S, red", "S, yellow", etc., etc.

This would of course give a list of 9 variants to choose from in the UI, but conceptually, the cashier just needs to make one choice of the size and one choice for the color. These two choices will then uniquely identify one of the 9 variants.

In the Ka-ching system we model this by a concept called dimensions. A product can contain any number of dimensions - and each of these dimensions can define a number of available dimension values. In the example above we could define the dimension: size with the values S, M and L and the dimension: color with the values red, yellow and blue.

By defining these, and adding a dimension value for each dimension to each of the variants, the POS can now display a much nicer UI - namely a list of dimensions, each having a number of possible values to select from.

In order to make the experience for the cashier even nicer, the dimension values can define both a color and also an image.

Example of a product with variants and dimensions:

{
    "products": [
        {
            "id": "0001",
            "name": "T-shirt",
            "retail_price": 150,
            "dimensions": [
                {
                    "id": "size",
                    "name": "Size",
                    "values": [
                        {
                            "id": "s",
                            "name": "S"
                        },
                        {
                            "id": "l",
                            "name": "L"
                        }
                    ]
                },
                {
                    "id": "color",
                    "name": "Color",
                    "values": [
                        {
                            "id": "red",
                            "name": "Red",
                            "color": "#FF0000",
                            "image_url": "https://url.for/red_cloth.image"
                        },
                        {
                            "id": "blue",
                            "name": "Blue",
                            "color": "#0000FF",
                            "image_url": "https://url.for/blue_cloth.image"
                        }
                    ]
                }
            ]
            "variants": [
                {
                    "id": "a",
                    "dimension_values": {
                        "color": "red",
                        "size": "s"
                    }
                },
                {
                    "id": "b",
                    "dimension_values": {
                        "color": "red",
                        "size": "l"
                    }
                },
                {
                    "id": "c",
                    "retail_price": 200,
                    "dimension_values": {
                        "color": "blue",
                        "size": "s"
                    }
                },
                {
                    "id": "d",
                    "retail_price": 200,
                    "dimension_values": {
                        "color": "blue",
                        "size": "l"
                    }
                }
            ]
        }
    ]
}

Notice how the blue t-shirts in this example are more expensive than the red ones.

Importing products to specific shops

If the imported products should not be shared between all shops, you can specify a list of shop identifiers as follows:

{
    "products": [
        {
            "id": "0001",
            "name": "Pie",
            "retail_price": 25
        }
    ],
    "shops": {
        "shop_a": true,
        "shop_b": true,
        "shop_c": true
    }
}

Deleting Products

Send an HTTP DELETE request to the endpoint with a body containing a JSON object containing product ids to delete.

{
  "ids": ["0001", "0002"]
}

In order to delete shop specific products you may additionally specify an array of shop ids in the DELETE request:

{
  "ids": ["0001", "0002"],
  "shops": ["xxx", "yyy"]
}

The Product Recommendation Import Endpoint

https://{BASE_URL}/imports/recommendations?recommendation_id=[RECOMMENDATION ID]

Like the Product Import, this endpoint uses Import Queues to handle all imported data.

For Product Recommendations, you can define multiple integrations where each integration corresponds to a specific kind of recommendation. For instance you could model both Upsale recommendations and Other customers also bought recommendations. Note that these kinds are just visual groupings with corresponding labels, so you are free to create any kind of recommendation lists you wish. For instance you could have a Curated, hand made recommendation kind as well.

So once the integration is configured, you can POST JSON data to the endpoint.

{
    "products": [
        {
            "product_id": "0001",
            "recommendations": [ "0002", "0010", "1234" ]
        },
        {
            "product_id": "0002",
            "recommendations": [ "0001", "0010", "1235" ]
        },
        ...
    ]
}

So if the above data is POSTed to the curated recommendation id, then when you show product details for the product with id "0001", you will see the curated recommendations for "0002", "0010" and "1234". Any products that don't exist at the time of presentation will be filtered away.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/recommendations?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Deleting Product Recommendations

Send an HTTP DELETE request to the endpoint with a body containing a JSON array of Discount Campaign ids. Note that you will still need the recommendation_id query parameter to specify the recommendation category.

[
  "0001", "0003"
]

Image Import

The Image Import varies from the above in that it has no direct endpoint. Instead an account can be configured to use image importing or not, and if image importing is enabled, then all referenced images will be processed by an image import queue and be placed in the Ka-ching system CDN.

There are both advantages and disadvantages to enabling image importing:

Advantages by using image importing

  • The Ka-ching system CDN is operated by Google and has nodes all over the world, so downloading resources is fast.
  • Images added to the Ka-ching system CDN will automatically have thumbnails generated
  • If content is deleted from your own CDN they will still be available in the Ka-ching CDN
  • The POS client lives in the iOS ecosystem where App Transport Security is enabled. This means that security for http requests are enforced, basically disallowing non-https request and https TLS versions that are deemed insecure. If your images are imported to the Ka-ching CDN, they will be served from a secure connection that is verified to run with App Transport Security.

Reasons for not using image importing

  • We have set a size limitation of 2048x2048 pixels in our image importer, since the memory pressure for the image processing is expensive. Hosting your own images circumvents this limitation although having larger images will also be costly in traffic to each of the POS clients.
  • If you have a fast and stable CDN for hosting your images - and they are already in an appropriate format, then you may as well continue to use that.

Product CSV Import

Simple products may be imported to the Ka-ching system through the Ka-ching Back Office using the CSV format.

Since the CSV format is by nature very -flat-, and our product model is basically a hierarchical structure, there is not a very good fit between what we can express in CSV and what our products can express.

The import itself is also a manual process, so this is only intended for getting started using the system real quick - or if you do not have an external PIM system and would like to use the Ka-ching system as the primary source for your products.

The CSV import is described in detail here: Product CSV Import.

Product Recommendation CSV Import

With the CSV format, you can target all configured recommendation categories at once. The rank describes the sorting order of the imported data for each product.

Format description:

product_id,recommended_product_id,recommendation_category_id,rank
0001,0002,curated,1
0001,0010,curated,2
0001,1234,curated,3
0002,0001,curated,1
0002,0010,curated,2
0002,1234,curated,3

Download sample

Stock CSV Import

Stock CSV Import is ONLY intended for initial setup. This is due to the fact that the Ka-ching system is built around traceability, and all stock modifications are logged with detailed information about the reasons for the stock change (e.g. sales, returns, reception of goods or resets due to a stock inventory count).

The CSV Import is logged as 'stock import reset' event, so it basically overwrites the existing stock counts and does not provide a lot of detailed information as to the reason.

For this reason it should be used very sparingly - ideally only when the system is initially configured.

The format is very simple:

Format description:

Products without variants:

product_id,quantity
0001,11
0002,20
1234,1

Download sample

Products with variants:

product_id,variant_id,quantity
0001,blue,11
0001,red,2
0002,medium,20
0002,large,32
0003,,10

Note that it is ok to leave out the variant id for products without variants in the latter form.

Download sample

The Stock Import Endpoint

The stock import endpoint is intended for the situation where the stock count needs to be overridden for one or more products. This can be necessary if the singular truth is placed in a third party system.

Each stock count imported through the stock endpoint will, if successfully imported, become a stock event of the type external_reset.

Endpoint:

https://{BASE_URL}/imports/stock

Importing stock counts

The body of the POST request must be a JSON object with the following layout:

Without variants:

{
    "stock_location_id": {
        "product1": 7,
        "product2": 7
    }
}

With variants:

{
    "stock_location_id": {
        "product1": 7,
        "product2": 7,
        "product3": {
            "variant1": 7,
            "variant2": 7
        }
    }
}

It is possible to import products that do not (yet) exist at a given stock location.

It is not possible to import stock numbers to a non existing stock location.

Upon success a HTTP 200 OK reponse will be given with body looking something like this:

{
    "status": "OK",
    "message": "Import of 2 stock value(s) initiated"
}

The number of values that has been parsed from the request will be noted in the message (as seen in the example above).

Import Queues

For each data type that can be imported, any number of import queues can be created. Using the import endpoints, it is possible to specify which import queue to target. This means that you could configure separate data sources and keep this distinct for the purpose of debugging and monitoring.

When an item lands in the import queue, it will immediately be processed by the Ka-ching system. This processing may either fail or succeed. All successfully processed items will be placed in a success log and all failed imports will be placed in a failure log.

At Ka-ching we can monitor the queues and logs if granted permission by you, and in the future it will also be possible for you to monitor these yourself. All items in the success and failure logs are annotated with time stamps and success or error messages.

Since the processing happens asynchronously and per processed item, it is not currently possible to get the import status back through the import endpoint. The queue logs need to be examined to determine the success / failure of the import.

Import Queue Security Model

When posting data to an import endpoint you need to provide an API key. The API key identifies access to a specific import queue. For each import queue, any number of API keys can be created, but through our Back Office, we reuse API keys between integrations by default. The API keys determines the appropriate access rights for writing data into the import queues.

All access to the import endpoints using a given API key are logged with the IP of the client and a timestamp in order to give us the possibility to monitor and prevent abuse.

The Discount Campaign Import Endpoint

https://{BASE_URL}/imports/discount_campaigns

This endpoint uses Import Queues to handle all imported data. This strategy is detailed in Import Queues below, but basically it allows you to configure multiple import sources, and all traffic on each import source is logged in both success and error cases so that the import operations can be monitored and debugged.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/products?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Advanced Discount Campaign Import Parameters

When importing Discount Campaigns you can additionally specify which Markets and Channels you wish to import for. The default market is dk (Denmark) and the default channel is pos - signifying the POS channel for all Shops in the Danish market.

These defaults can be overridden by the query parameters markets and channels respectively. The value for both parameters are comma separated lists of markets and channels.

E.g.:

https://{BASE_URL}/imports/discount_campaigns?markets=dk,se,no&channels=pos,online&...

For further clarification of the Market and Channel concepts, please read the Vocabulary.

Importing Discount Campaigns

Send a POST request to the endpoint with the parameters specified above.

The body of the POST request must be a JSON object with the following layout:

{
    "campaigns": {
        [campaign A],
        [campaign B],
        ...
    }
}

The definition of the Discount Campaign models can be found in the Models document. Here is an example with mixed discount campaign types:

{
    "campaigns": [
        {
            "id": "0001",
            "type": "percentage_discount-count_or_more-single_product",
            "product_id": "abc",
            "percentage": 0.42,
            "name": "ABC discount",
            "display_name": "Discount",
            "count": 3,
            "priority": 40
        },
        {
            "id": "0002",
            "type": "percentage_discount-tag",
            "tag": "clothing",
            "percentage": 0.42,
            "name": "Clothes discount",
            "display_name": "Clothes discount",
            "priority": 60
        },
        {
            "id": "0003",
            "type": "new_price_discount-single_product",
            "product_id": "abc",
            "new_price_per_item": {"dk": 42, "norge": 60},
            "name": "New price discount",
            "display_name": "New price discount",
            "priority": 80
        }
    ]
}

NOTE

When discount campaigns are imported into the Ka-ching system, the id will be used as a unique key.

One limitation in the database we are using (the Firebase real-time database) is that not all characters are valid to use in keys.

This means that the following list of characters may not be used in the "id" field of a product: ., /, #, $, [ and ]

Campaign Types: Percentage by condition on count

If you wish to give a discount to one or more products based on the presence of a specific count (or more) of a product you can use the types percentage_discount-count_or_more-single_product and percentage_discount-count_or_more-multiple_products:

{
    "campaigns": [
        {
            "id": "0001",
            "type": "percentage_discount-count_or_more-single_product",
            "product_id": "abc",
            "percentage": 0.42,
            "name": "ABC discount",
            "display_name": "Discount",
            "count": 3,
            "priority": 40
        }
    ]
}

This will give a 42% discount on the product with id abc if there are 3 or more of these in the basket.

{
    "campaigns": [
        {
            "id": "0001",
            "type": "percentage_discount-count_or_more-multiple_products",
            "product_ids": ["abc", "def"],
            "percentage": 0.42,
            "name": "ABC discount",
            "display_name": "Discount",
            "count": 3,
            "priority": 40
        }
    ]
}

This will give a 42% discount on the products with either id abc or def if there are 3 or more of these in the basket in total. This will then trigger if there is 1 * abc and 2 * def.

Campaign Types: Percentage by presence of tag

{
    "campaigns": [
        {
            "id": "0002",
            "type": "percentage_discount-tag",
            "tag": "clothing",
            "percentage": 0.42,
            "name": "Clothes discount",
            "display_name": "Clothes discount",
            "priority": 60
        }
    ]
}

This will give a 42% discount unconditionally for products that contain the tag clothing.

Campaign Types: Percentage by presence of tag with condition on count

{
    "campaigns": [
        {
            "id": "0003",
            "type": "percentage_discount-count_or_more-tag",
            "tag": "clothing",
            "percentage": 0.42,
            "name": "Clothes discount",
            "display_name": "Clothes discount",
            "count": 3,
            "priority": 80
        }
    ]
}

This will give a 42% discount for products that contain the tag clothing but only if there are 3 or more items with that tag.

Campaign Types: New price by product

{
    "campaigns": [
        {
            "id": "0003",
            "type": "new_price_discount-single_product",
            "product_id": "abc",
            "new_price_per_item": 42,
            "name": "New price discount",
            "display_name": "New price discount",
            "priority": 80
        }
    ]
}

This will give a 'new price' discount unconditionally for the product with id 'abc'. This is similar to modelling a sale price directly on the product.

Campaign Types: New price by product with condition on count

{
    "campaigns": [
        {
            "id": "0003",
            "type": "new_price_discount-count_or_more-single_product",
            "product_id": "abc",
            "new_price_per_item": 42,
            "name": "New price discount",
            "display_name": "New price discount",
            "count": 3,
            "priority": 80
        }
    ]
}

This will give a 'new price' discount for the product with id 'abc', but only if there are 3 or more items that id.

Campaign Types: New price 'stair' for a single product

{
    "campaigns": [
        {
            "id": "0003",
            "type": "new_price_discount-stair-single_product",
            "product_id": "abc",
            "name": "Price stair",
            "display_name": "Price stair",
            "steps": [
                { "count": 3, "new_price_per_item": 100 },
                { "count": 6, "new_price_per_item": 90 },
                { "count": 9, "new_price_per_item": 80 }
                ],
            "priority": 80
        }
    ]
}

This will create a 'new price' stair for the product with id 'abc'. This means that for 1 or 2 items, the price will be the default. For 3-5 or more it will be 100, for 6-8 it will be 90 and for 9 or more it will be 80.

Campaign Types: Percentage discount 'stair' for a single product

{
    "campaigns": [
        {
            "id": "0003",
            "type": "percentage_discount-stair-single_product",
            "product_id": "abc",
            "name": "Percentage stair",
            "display_name": "Percentage stair",
            "steps": [
                { "count": 3, "percentage": 0.1 },
                { "count": 6, "percentage": 0.15 },
                { "count": 9, "percentage": 0.2  }
                ],
            "priority": 80
        }
    ]
}

This will create a 'percentage' stair for the product with id 'abc'. This means that for 1 or 2 items, the price will be the default. For 3-5 or more it will be discounted with 10%, for 6-8 it will be discounted with 15% and for 9 or more it will be discounted with 20%.

Campaign Types: Percentage discount 'stair' for a products matching a certain tag

{
    "campaigns": [
        {
            "id": "0003",
            "type": "percentage_discount-stair-tag",
            "tag": "clothing",
            "name": "Percentage stair",
            "display_name": "Percentage stair",
            "steps": [
                { "count": 3, "percentage": 0.1 },
                { "count": 6, "percentage": 0.15 },
                { "count": 9, "percentage": 0.2  }
                ],
            "priority": 80
        }
    ]
}

This will create a 'percentage' stair for the products matching the tag 'clothing'. This means that for 1 or 2 items, the price will be the default. For 3-5 or more it will be discounted with 10%, for 6-8 it will be discounted with 15% and for 9 or more it will be discounted with 20%.

Discount Campaigns across multiple markets

When using this type of campaign across different markets, it becomes necessary to specify the new price in each of the markets. This can be done as follows:

{
    "campaigns": [
        {
            "id": "0003",
            "type": "new_price_discount-single_product",
            "product_id": "abc",
            "new_price_per_item": { "dk": 42, "no": 60 },
            "name": "New price discount",
            "display_name": "New price discount",
            "priority": 80
        }
    ]
}

A discount stair could look as follows in multiple markets:

{
    "campaigns": [
         {
            "id": "0002",
            "type": "new_price_discount-stair-single_product",
            "product_id": "abc",
            "name": "ABC discount",
            "display_name": "Discount",
            "steps": [
                { "count": 3, "new_price_per_item": { "dk": 100, "norge": 150 } },
                { "count": 6, "new_price_per_item": { "dk": 90, "norge": 130 } },
                { "count": 9, "new_price_per_item": { "dk": 80, "norge": 110 }  }
                ],
            "priority": 50
        }
    ]
}

Creating 'member only' discounts

For any discount campaign template, you may specify 'members only' on the template. The interpretation of this field is that the discount will only apply if there is a customer attached to the current sale.

{
    "campaigns": [
          {
            "id": "0003",
            "type": "percentage_discount-stair-tag",
            "tag": "vin",
            "name": "Member discount",
            "members_only": true,
            "display_name": "Member discount",
            "steps": [
                { "count": 3, "percentage": 0.1 },
                { "count": 6, "percentage": 0.15 },
                { "count": 9, "percentage": 0.2  }
                ],
            "priority": 10
        }
    ]
}

The discount stair above will only apply when a customer is present on the sale.

Allowing multiple discounts on the same item

For any discount campaign template, you may specify 'continue evaluation' on the template. The interpretation of this field is that even after applying this specific discount, the same item can receive other discounts as well.

{
    "campaigns": [
        {
            "id": "0003",
            "type": "new_price_discount-single_product",
            "product_id": "merlot",
            "new_price_per_item": 100,
            "name": "New price discount",
            "display_name": "New price discount",
            "members_only": true,
            "continue_evaluation": true,
            "priority": 80
        },
        {
            "id": "0004",
            "type": "percentage_discount-stair-tag",
            "tag": "wine",
            "name": "Percentage discount",
            "display_name": "Percentage discount",
            "steps": [
                { "count": 3, "percentage": 0.1 },
                { "count": 6, "percentage": 0.15 },
                { "count": 9, "percentage": 0.2  }
                ],
            "priority": 10
        }
    ]
}

Imagine that you sell a bottle of wine with the product id: "merlot". The product is tagged with "wine" and it costs 150 DKK.

The first discount (0003) will only trigger for members and will reduce the price to 100 per bottle. If you buy 6 of these, you will furthermore get a discount (from discount 0004) of 15% on top of the discountet price.

The total will be (100 * 6) * (1 - 0.15) = 510 DKK (85 DKK per bottle)

Deleting Discount Campaigns

Send an HTTP DELETE request to the endpoint with a body containing a JSON array of Discount Campaign ids.

[
  "0001", "0003"
]

The Product Groups Import Endpoint

https://{BASE_URL}/imports/product_groups

Like the Product Import, this endpoint uses Import Queues to handle all imported data.

You may post any amount of Product Groups to the endpoint. Existing product groups are overwritten based on their id.

[
    {
        "group": "dairy",
        "name": { "da": "Mælkeprodukter", "en": "Dairy" }
    },
    {
        "group": "misc",
        "name": "Misc."
    },
    ...
]

The name is an L10nString and may thus be supplied with or without localizations.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/product_groups?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Deleting Product Groups

Send an HTTP DELETE request to the endpoint with a body containing a JSON array of Product Group ids.

[
  "dairy", "misc"
]

The Tags Import Endpoint

https://{BASE_URL}/imports/tags

Like the Product Import, this endpoint uses Import Queues to handle all imported data.

You may post any amount of Tags to the endpoint. Existing tags are overwritten based on their id.

[
    {
        "tag": "popular",
        "name": { "da": "Populære", "en": "Popular" }
    },
    {
        "tag": "new",
        "name": { "da": "Nye varer", "en": "New products" }
    },
    ...
]

The name is an L10nString and may thus be supplied with or without localizations.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/tags?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Deleting Tags

Send an HTTP DELETE request to the endpoint with a body containing a JSON array of Tag ids.

[
  "new", "popular"
]

The Attributes Import Endpoint

https://{BASE_URL}/imports/attributes

Like the Product Import, this endpoint uses Import Queues to handle all imported data.

You may post any amount of Attributes to the endpoint. Existing attributes are overwritten based on their id.

[
    {
        "id": "country",
        "name": { "da": "Land", "en": "Country" },
        "type": {
            "options": {
                "denmark": {
                    "id": "denmark",
                    "name": { "da": "Danmark", "en": "Denmark" }
                },
                "france": {
                    "id": "france",
                    "name": { "da": "Frankrig", "en": "France" }
                }
            }
        }
    },
    {
        "id": "grape",
        "name": { "da": "Drue", "en": "Grape" },
        "type": {
            "options": {
                "cabernet": {
                    "id": "cabernet",
                    "name": "Cabernet Sauvignon"
                },
                "merlot": {
                    "id": "merlot",
                    "name": "Merlot"
                }
            }
        }
    },
    ...
]

The name is an L10nString and may thus be supplied with or without localizations.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/attributes?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Deleting Attributes

Send an HTTP DELETE request to the endpoint with a body containing a JSON array of Attribute ids.

[
  "country", "grape"
]

The Folders Import Endpoint

https://{BASE_URL}/imports/folders

Like the Product Import, this endpoint uses Import Queues to handle all imported data.

The folder hierarchy is just a single value containing the entire hierarchy. This means that any post to this endpoint will overwrite the current folder hierarchy with the newly posted one.

[
    {
        "filter": {
            "tag": "usa"
        },
        "children": [
            {
                "filter": {
                    "tag": "californien"
                },
                "children": [
                    {
                        "filter": {
                            "tag": "anderson_valley"
                        }
                    },
                    {
                        "filter": {
                            "tag": "carneros"
                        }
                    }
                ]
            }
        ]
    }
]

The folders hierarchy is described by an array of filters. Along with each filter you may supply arrays of children of the same structure (a filter and an optional array of children). The filters can currently specify tags that much be matched for products to be included in the folder. This may be expanded upon in the future.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/folders?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Deleting Folders

Sincere there only exists one hierarchy of folders, sendin an HTTP DELETE request to the endpoint without any contents will delete the folders.

The Tabs Import Endpoint

https://{BASE_URL}/imports/tabs

Like the Product Import, this endpoint uses Import Queues to handle all imported data.

The tabs list is just a single value containing an array of filters. This means that any post to this endpoint will overwrite the current tabs with the newly posted one.

[
    {
        "filter": {
            "tag": "new"
        }
    },
    {
        "filter": {
            "tag": "popular"
        }
    },  
    {
        "filter": {
            "tag": "recommended"
        }
    },
    {
        "filter": {
            "tag": "offers"
        }
    }
]

The tabs is described by an array of filters. The filters can currently specify tags that much be matched for products to be included on the selected tab. This may be expanded upon in the future.

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/tabs?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Deleting Tabs

Sincere there only exists one array of tabs, sendin an HTTP DELETE request to the endpoint without any contents will delete the tabs.

The Recommendation Categories Import Endpoint

https://{BASE_URL}/imports/recommendation_categories

Like the Product Import, this endpoint uses Import Queues to handle all imported data.

You may post any amount of Recommendation Categories to the endpoint. Existing categories are overwritten based on their id.

[
    {
        "id": "similar",
        "name": "Similar"
    },
    {
        "id": "also_bought",
        "name": {
            "en": "Other customers also bought",
            "da": "Andre kunder købte også"
        }
    }
]

Shared Integration Queue Parameters

For all integration queue import endpoints, you need to specify account, integration and apikey. Please read the Import Queues below for details about these parameters.

https://{BASE_URL}/imports/recommendation_categories?account=[ACCOUNT ID]&integration=[INTEGRATION QUEUE ID]&apikey=[INTEGRATION QUEUE API KEY]

Deleting Recommendation Categories

Send an HTTP DELETE request to the endpoint with a body containing a JSON array of Recommendation Category ids.

[
  "similar", "upsale"
]