Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Product Management

Products are organized in a three-level hierarchy: Group → Department → PLU (product). Tags provide an additional cross-cutting grouping mechanism, and menus bundle products into meals or combos.

Group (grp)
├── Department (dpt)
│ ├── PLU (product) — type 1: Sales
│ ├── PLU (product) — type 2: Ingredient
│ └── PLU (product) — type 8: Prepared
└── Department (dpt)
├── PLU (product)
└── PLU (product)

Each product belongs to exactly one department, and each department belongs to exactly one group. All three entities are scoped to a company.

Groups are the top-level product category.

FieldTypeDescription
grp_numintUnique group number
namestringGroup name
data.sales_grpbooleanContains sales departments
data.stock_grpbooleanContains stock/inventory departments
data.image_urlstringImage for menu exports
data.sort_ordernumberDisplay order in menu exports

Departments sit between groups and products.

FieldTypeDescription
dpt_numnumberUnique department number
grp_ididParent group reference
namestringDepartment name
data.sales_dptbooleanSales department
data.stock_dptbooleanStock/inventory department
data.default_taxnumberDefault tax rate for new products in this department
data.colorstringButton color on POS
data.image_urlstringImage for menu exports
data.sort_ordernumberDisplay order in menu exports

The PLU is the core product entity.

FieldTypeDescription
productidstringUnique product number (often auto-generated from department template)
namestringProduct name (translatable)
receipt_namestringShortened name for receipts (translatable)
typeintProduct type (see below)
base_pricenumberBase selling price (tax-inclusive)
dpt_ididParent department
tax_ididTax rate reference
image_idstringPrimary product image (attachment ID)
memostringFree text description (translatable)
TypeNameDescription
1SalesStandard sellable product
2IngredientRaw material / ingredient for recipes
3ServiceService item
4WastageWastage tracking item
5ReportingReporting-only item
6StockStock management item
8PreparedPrepared/composite product (has recipe)

Products carry extensive configuration in plu.data:

  • flags — Boolean toggles: askprice, kitchenprinter, nosell, no_discount, modifier, giftcard, scale, alcohol, etc.
  • barcode / barcodes — Main barcode and additional barcodes with optional custom quantity/price
  • recipe — Ingredient list for composite products (type 1 & 8), each with multiplier, price, and tax reference
  • stock_units — Multiple units of measure (pcs, weight, liquid) with stock quantities and supplier info
  • supply — Supplier information for ordering
  • allergens — EU 14 allergen flags (cereals_gluten, crustaceans, eggs, fish, peanuts, soybeans, milk, nuts, celery, mustard, sesame, sulphites, lupin, molluscs)
  • nutrition — Nutritional information (energy, fats, carbohydrates, protein, salt, vitamins, etc.)
  • cost_price — Purchase price (tax-free), used for margin calculations
  • sales_channels — Which sales channels this product is available in

The plu.data.tags field (string array) provides a flexible grouping mechanism that is independent of the Group → Department hierarchy.

Tags are defined at the company level (company.data.default_tags) and can be freely assigned to any product regardless of its department. This enables cross-cutting categorizations such as:

  • Dietary: vegan, gluten-free, lactose-free
  • Menu sections: breakfast, lunch, dessert
  • Channels: app-menu, takeaway
  • Promotions: happy-hour, seasonal

Tags are used in several places:

  • Menu item selection — A menu item can select products by tag (via taglist) instead of listing individual PLU IDs
  • Filtering and reporting — Filter product lists by tag in the management UI
  • External integrations — Tags can drive which products appear in online ordering or delivery platforms

A menu bundles multiple product selections into a single sellable unit — for example a meal deal or a combo.

A menu contains an array of menu items, each defining one selection step:

FieldTypeDescription
typeenumDisplay type: POPUP, LIST, FOLDABLE, LAYOUT, PICKER, HIDDEN
plulistguid[]Specific products to choose from
dptlistguid[]Select from all products in these departments
tagliststring[]Select from products matching these tags
optionalbooleanWhether the selection can be skipped
min_qty / max_qtynumberQuantity constraints
default_pluguidPre-selected product
pricingplu_pricingOverride pricing for this step
price_levelguidUse an alternate price level
group_pricebooleanInclude this item’s price in the main product

A “Hamburger Meal” menu with three steps:

Menu: Hamburger Meal
├── Item 1: Burger (required)
│ plulist: [Classic Burger, Cheese Burger, Veggie Burger]
│ optional: false, min_qty: 1, max_qty: 1
├── Item 2: Side (required)
│ taglist: ["sides"]
│ optional: false, min_qty: 1, max_qty: 1
│ pricing: { type: "PRICE", amount: 0 } ← included in meal price
└── Item 3: Drink (optional)
dptlist: [Drinks department]
optional: true, min_qty: 0, max_qty: 1
pricing: { type: "PRICE", amount: 2.50 } ← fixed add-on price

Product selection within a menu item can use any combination of plulist, dptlist, and taglist. The pricing override on each item controls whether the component is included in the base price or charged separately.

The menu’s type field controls the overall UI flow: SINGLE for a simple combo, WIZARD for a step-by-step builder.


Pricing entries define named price levels that can be assigned to products.

FieldTypeDescription
namestringPrice level name (e.g. “Self-Service”, “Happy Hour”, “VIP”)
data.typeenumbaseprice, price, discount, percent, purchaseprice
data.shortnamestringShort display name
data.set_taxtypebooleanWhether this price level overrides the tax rate
data.taxtypeguidTax rate override (when set_taxtype is true)

Each product stores its price level values in plu.data.prices, keyed by the pricing entry’s GUID:

{
"data": {
"prices": {
"pricing-guid-1": {
"type": "PRICE",
"amount": 8.50
},
"pricing-guid-2": {
"type": "PERCENT",
"amount": 90
}
}
}
}
TypeEffect
basepriceUse the product’s base price (no change)
priceFixed alternative price
discountSubtract amount from base price
percentPercentage of base price (e.g. 90 = 90% of base price)
purchasepriceCost/purchase price (for stock valuation)
  • Self-service channel — A “Self-Service” price level with lower prices, assigned to self-service POS terminals
  • Customer-specific pricing — A “VIP” or “Wholesale” price level assigned to customer groups
  • Happy hour — A “Happy Hour” price level with discounted prices, activated by time-based rules on the POS
  • POS-selectable — Cashier manually selects a price level on the terminal (e.g. for staff meals)

Price levels are subscribed per POS terminal via pos.data.subscriptions, so each terminal can offer different price levels.


The tax data is shared across all companies and cannot be edited through the API — it is maintained centrally.

Products reference a tax rate via plu.tax_id. The current Finnish VAT rates are:

NameRatetax_numValid From
ALV 0%0%41990-01-01
ALV 10%10%32013-01-01
ALV 13.5%13.5%22025-12-01
ALV 25.5%25.5%12024-09-01

Tax ←──────── PLU (product) ──────→ Department ──────→ Group
├── data.tags[] (cross-cutting grouping)
├── data.prices{} (pricing per price level)
├── data.recipe[] (ingredient links to other PLUs)
├── data.sales_channels[] (channel availability)
└── data.menu_items[] (menu associations)
Menu
└── items[] ──→ selects PLUs by plulist / dptlist / taglist
Pricing ──→ referenced in plu.data.prices{} and menu item overrides