Skip to content

article

An article is an item kept in stock: a product, a tool, a spare part. Stock is not a field of the article; it is the sum of the movements that book it.

  • Mutable: the version with the greater hlc wins.
  • Ids: random UUIDs.
  • Deleting: an article that a movement references is archived (archived: true) instead of deleted, so that history keeps its name. The reference app deletes (tombstone) only articles without movements and refuses to delete or archive an article that has stock.
Field Type Writer Default Meaning
sku string server Item number. Not unique across the inventory (see below).
name string server Display name
description string? optional none Longer text
category string? optional none Free text, used for grouping and report filters
manufacturer string? optional none
unit string? optional Stk Unit of all quantities of this article, free text (Stk, m, kg, Pkg)
track_serials boolean? optional false Every unit has a serial number; movements carry one serial per unit
min_stock number? optional none The article is low on stock while its total stock is below this value
purchase_price number? optional none Net purchase price per unit in the inventory currency, at least 0
sale_price number? optional none Net sales price per unit in the inventory currency, at least 0
accessories array of string? optional [] What belongs to one unit (charger, case); offered when the article is lent
barcodes array? optional [] Codes printed on the article, see below
image sha256? optional none Attachment: JPEG photo
erp_reference string? optional none Key of the article in another system
archived boolean? optional false Hidden from lists and pickers
created_at date-time? optional none When the first device created the record; display only
Field Type Writer Meaning
code string server Content of the code exactly as scanned
symbology string? optional Kind of code, lower case, for example ean13, ean8, upce, code128, code39, qr, datamatrix

Barcodes are compared exactly. How the reference app resolves a scanned code (barcodes, GS1 codes with serial numbers, item numbers, its own label URLs) is described under Scanning codes.

  • sku and name are trimmed and must not be empty.
  • Item numbers and barcodes SHOULD be unique. The reference app refuses an item number that another article on the device already has (compared case-insensitively) and a barcode another article already uses. Two devices offline can still create the same number; servers and readers MUST accept that.
  • track_serials SHOULD NOT change while the article has stock. The reference app refuses the change in that case, because existing movements would no longer match the rule "one serial per unit".
  • Prices are not negative.
  • accessories are trimmed, without blanks, without duplicates (case-insensitive), in the order the user typed them.
  • created_at is set once when the record is created and kept on every later version.
Value Rule
Stock per location Sum of movements, see Derived state
Total stock Sum of the stock rows
Low on stock min_stock set and total stock below it
Serial numbers State from the latest movement containing each serial
Last changed Time part of hlc
Field Rules
sku, name required, string
description, category, manufacturer, erp_reference, unit nullable, string
archived, track_serials nullable, boolean
min_stock nullable, numeric
purchase_price, sale_price nullable, numeric, at least 0
accessories nullable, list; each element a string
barcodes nullable, list; each element an object with code (required, string) and symbology (nullable, string)
image nullable, string matching ^[0-9a-f]{64}$
{
"entity": "article",
"id": "4A7C2E91-0B3D-4F6A-8E15-7C9B2D4F6A80",
"op": "upsert",
"hlc": "001791792000123-0000-3F2A9C1B",
"data": {
"sku": "200310",
"name": "Cordless drill 18 V",
"category": "Tools",
"manufacturer": "Example Tools",
"unit": "Stk",
"track_serials": true,
"min_stock": 2,
"purchase_price": 189.9,
"sale_price": 249,
"accessories": ["Charger", "Battery 4 Ah", "Case"],
"barcodes": [{ "code": "4006381333931", "symbology": "ean13" }],
"image": "35a12c5c6f0150bef53f609d70846b1a7568a44d5565b903d2f536b0c4ac5fdf",
"created_at": "2026-10-12T08:00:00Z"
}
}

A minimal article that every reader accepts: { "sku": "100101", "name": "Wireless access point AP-7" }.

schemas/article.json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Article",
"description": "Item master data. Mutable: the version with the greater hlc wins. Articles that movements reference are archived instead of deleted.",
"type": "object",
"required": ["sku", "name"],
"properties": {
"sku": { "description": "Item number. Not unique across the inventory: two devices may create the same number offline. The reference app refuses a duplicate (case-insensitive) on the device that types it.", "type": "string", "minLength": 1 },
"name": { "description": "Display name.", "type": "string", "minLength": 1 },
"description": { "type": ["string", "null"] },
"category": { "description": "Free text, used for grouping and report filters.", "type": ["string", "null"] },
"manufacturer": { "type": ["string", "null"] },
"erp_reference": { "description": "Key of the article in another system (an ERP, for example).", "type": ["string", "null"] },
"archived": { "description": "Hidden from lists and pickers; used instead of deleting once movements reference the record. Absent or null means false.", "type": ["boolean", "null"] },
"unit": { "description": "Unit of quantities, free text. Absent or null means \"Stk\" (German for pieces).", "type": ["string", "null"], "default": "Stk" },
"track_serials": { "description": "Movements of this article carry one serial number per unit. Absent or null means false.", "type": ["boolean", "null"], "default": false },
"min_stock": { "description": "The article counts as low on stock while its total stock is below this value.", "type": ["number", "null"] },
"purchase_price": { "description": "Net purchase price per unit in the inventory currency (company_profile.currency).", "type": ["number", "null"], "minimum": 0 },
"sale_price": { "description": "Net sales price per unit in the inventory currency.", "type": ["number", "null"], "minimum": 0 },
"accessories": { "description": "What belongs to the article (power supply, bag, ...); suggested when it is lent. Absent or null means an empty list.", "type": ["array", "null"], "items": { "type": "string", "minLength": 1 }, "default": [] },
"barcodes": {
"description": "Codes printed on the article. Absent or null means an empty list.",
"type": ["array", "null"],
"default": [],
"items": {
"type": "object",
"required": ["code"],
"properties": {
"code": { "description": "Content of the code exactly as scanned.", "type": "string", "minLength": 1 },
"symbology": { "description": "Kind of code as reported by the scanner, lower case.", "type": ["string", "null"], "examples": ["ean13", "ean8", "upce", "code128", "code39", "qr", "datamatrix"] }
},
"additionalProperties": true
}
},
"image": {
"description": "SHA-256 of a JPEG attachment showing the article.",
"type": ["string", "null"],
"pattern": "^[0-9a-f]{64}$"
},
"created_at": { "description": "When the record was first created, set once by the creating device. Display only.", "type": ["string", "null"], "format": "date-time" }
},
"additionalProperties": true,
"examples": [
{
"sku": "200310",
"name": "Cordless drill 18 V",
"category": "Tools",
"manufacturer": "Example Tools",
"unit": "Stk",
"track_serials": true,
"min_stock": 2,
"purchase_price": 189.9,
"sale_price": 249,
"accessories": ["Charger", "Battery 4 Ah", "Case"],
"barcodes": [{ "code": "4006381333931", "symbology": "ean13" }],
"image": "35a12c5c6f0150bef53f609d70846b1a7568a44d5565b903d2f536b0c4ac5fdf",
"created_at": "2026-10-12T08:00:00Z"
}
]
}