Cliento API reference: Products and stock

Base URL: https://api.cliento.com

The Products and stock APIs are for an integration that keeps track of a location's stock. It can read the products and their balances, and record deliveries and withdrawals.

A location has one product list and one stock. All the location's merchants share them.

  • Products. A product is an article the location sells or uses. A product with stock tracking has a balance: the number of units the location has.
  • Stock movements. A stock movement is one change to a balance. The movements are the history behind every balance.

A balance changes in several ways. These are the common ones:

  • A sale at a point of sale takes out what is sold, whichever merchant sells it.
  • A delivery adds to it.
  • Staff take out what they use.
  • A stocktake counts the stock and corrects the balance to what was counted.

A hair salon's colours, developers and bleaches are not products. They are colour bar items, described under Colour bar. Their balances are in the same stock, so a stock movement names either a product or a colour bar item.

The Products API reads the product list and the balances. The Stock movements API reads the history and records deliveries and withdrawals. The APIs for points of sale, sales and stocktakes are coming soon.

Products

The Products API lists a location's products, each with its current stock balance. Use it to show what a location sells and has in stock, or to look a product up from a scanned barcode or a delivery note.

A product is an article the location sells or uses. A product with stock tracking has a balance. The Products API returns only the current balance. What changed it is read from the Stock movements API.

A product belongs to one location and has an id of the form prd_.... GET /v1/locations/{locationId}/products lists the products a page at a time. Reading needs the scope products:read.

  • To look one product up, add ean or articleNumber to the list.
  • Quantities are whole numbers: ten bottles are a stockQuantity of 10.
  • stockQuantity is there only for a product the location keeps stock of (stockTracked).
  • Purchase prices (latestPurchasePrice, averageCost) are returned only to a key that also has the scope stock:cost.

List products

GET /v1/locations/{locationId}/products

Scope: products:read

Stability: preview

The location's products, one page at a time, including those that are not shown on the point-of-sale screen as products that can be sold. To read them all, repeat the call with after set to the nextCursor of the previous page until a page has none. With ean or articleNumber it is a lookup: the product a scanned barcode or a delivery note row belongs to.

Parameters

NameInTypeRequiredDescription
locationIdpathstringYes
limitqueryinteger (int32)NoPage size, 1 to 200. Default 50.
afterquerystringNoThe nextCursor of the previous page.
eanquerystringNoOnly products with this barcode. Normally one; Two can match, because a product also has the barcodes of the supplier's article it is linked to; see eans.
articleNumberquerystringNoOnly products with exactly this article number.

Responses

StatusDescriptionBody
200OKProductList
defaultAny failure. See the Errors section.Error

Get a product

GET /v1/locations/{locationId}/products/{productId}

Scope: products:read

Stability: preview

Parameters

NameInTypeRequiredDescription
locationIdpathstringYes
productIdpathstringYes

Responses

StatusDescriptionBody
200OKProduct
defaultAny failure. See the Errors section.Error

Get a product by id

GET /v1/products/{productId}

Scope: products:read

Stability: preview

The same product as under its location, found from its id. For a caller that holds an id and not the location it belongs to. The answer carries locationId.

Parameters

NameInTypeRequiredDescription
productIdpathstringYes

Responses

StatusDescriptionBody
200OKProduct
defaultAny failure. See the Errors section.Error

Stock movements

The Stock movements API lets an integration read every change to a location's stock, and record deliveries and withdrawals of its own. Use it when you need to know what changed a balance and why, or when you keep your own copy of the balances. The Products API returns only the current balance.

A location has one stock. Every product with stock tracking has a balance in it. A balance is never set directly. It changes when something happens, such as:

  • A merchant sells the product at a point of sale.
  • A delivery arrives.
  • Staff take out what they use.
  • A stocktake corrects the balance to what was counted.

A stock movement is the record of one such change to one balance. A movement is never changed or removed. A mistake is undone by a new movement that corrects it.

Movements made by a sale or a stocktake are read here too. The Sales API and the Stocktakes API are coming soon.

A colour bar item has a balance in the same stock, counted in packs. A change to it is a stock movement too. A movement has a productId or a colourBarItemId, never both. When a colour bar mix finishes a pack, the pack leaves the stock as a withdrawal with the mix's colourBarMixId. Such a movement is not corrected on its own; the mix is undone.

A stock movement belongs to one location and has an id of the form mov_.... GET /v1/locations/{locationId}/stock-movements lists the movements oldest first. Reading needs the scope stock:read.

The list can be followed. Keep the id of the last movement you have read and pass it as after to get what has happened since. That is how to keep your own copy of the balances up to date without reading every product again. A movement appears in the list about ten seconds after it is made.

Recording movements

POST /v1/locations/{locationId}/stock-movements records a stock document: one or more stock changes that belong together, such as the rows of a delivery note, or products taken from the shelf for the location's own use. It needs the scope stock:write.

  • A delivery or withdrawal line names a product or a colour bar item, with productId or colourBarItemId. For a colour bar item the quantity is whole packs: a delivery of twelve tubes is 12. A correction line names neither, only the movement it undoes.
  • Every line is applied, or none. A line that takes out more than is in stock gets the whole document refused with insufficient_stock; details names the product or the colour bar item, its balance and the quantity asked for.
  • listValue is what a withdrawal is worth at the supplier's list price, VAT excluded. It is there when what was taken is linked to an article in a supplier's catalogue that has a list price. A location uses it to charge staff for what they take for themselves, with the reason own-use.
  • Give the document your own externalReference, such as the delivery note number. Sending the same reference again changes nothing and answers 200 with the movements made the first time, however long ago.
  • staffId names the staff member who made the change. Read it from the Staff API: from the staff list, or from the PIN check.
  • Two line types need a further scope. A correction undoes a withdrawal and needs stock:correct. A delivery sets what the goods cost and needs stock:cost. A key for a screen where staff record what they use needs neither.

List stock movements

GET /v1/locations/{locationId}/stock-movements

Scope: stock:read

Stability: preview

Every change of a stock balance at the location, oldest first, including sales at the point of sale. The list can be followed: keep the id of the last movement you have read and pass it as after to get what has happened since. A movement appears in the list about ten seconds after it is made.

Parameters

NameInTypeRequiredDescription
locationIdpathstringYes
limitqueryinteger (int32)NoPage size, 1 to 200. Default 50.
afterquerystringNoOnly movements made after this one: a nextCursor or the id of any movement.
productIdquerystringNoOnly this product's movements.
colourBarItemIdquerystringNoOnly this colour bar item's movements.
typequerystringNoOnly movements of this type.
externalReferencequerystringNoOnly movements with this external reference.

Responses

StatusDescriptionBody
200OKStockMovementList
defaultAny failure. See the Errors section.Error

Record stock movements

POST /v1/locations/{locationId}/stock-movements

Scope: stock:write

Stability: preview

Records a document of stock changes: a delivery note, products taken from the shelf for the location's own use. Every line is applied, or none: a line that takes out more than is in stock gets the whole document refused with insufficient_stock. Answers 201 with the movements made, and 200 with the movements made the first time when externalReference was used before. A correction line needs the scope stock:correct as well, and a delivery line the scope stock:cost, since it sets what the goods cost.

Parameters

NameInTypeRequiredDescription
locationIdpathstringYes
Idempotency-KeyheaderstringNoA value you make up for this one request, such as a UUID. Send the same value when you retry and the first answer is returned; see Retrying safely. Example: 8e03978e-40d5-43e8-bc93-6894a57f9324.

Request body: StockDocument

Responses

StatusDescriptionBody
200The externalReference was used before. Nothing in this request was applied; the movements made then are returned.StockDocumentResult
201The movements were made.StockDocumentResult
defaultAny failure. See the Errors section.Error

Stocktakes

Coming soon. Start a count of a location's stock, send what was counted and read the differences.

Schemas

Error

The body of every non-2xx response.

FieldTypeRequiredDescription
codestringYesStable, machine-readable error code. Values today: invalid_request, validation_failed, entity_not_found, duplicate, merchant_required, code_already_used, gift_card_already_used, pin_not_recognised, insufficient_stock, not_allowed, request_in_progress, idempotency_key_reused, unauthenticated, forbidden, account_blocked, rate_limited, route_not_found, method_not_allowed, internal_error.
detailsobject of any valueNoExtra detail. For validation_failed, fieldErrors maps each rejected field to the reason.
messagestringYesHuman-readable explanation. Not stable; do not parse it.

Product

Represents an article a location sells or uses: an entry in its product list. Belongs to that location. Amounts are decimal strings in the location's currency. Quantities are whole numbers of the product.

FieldTypeRequiredDescription
articleNumberstringNoThe supplier's article number, as read from a delivery note. Not unique: two suppliers can use the same one. Example: 81650673.
averageCoststringNoThe average cost of those in stock, VAT excluded. Only with the scope stock:cost. Example: 61.2311.
brandstringNoExample: Wella.
createdAtstring (date-time)NoWhen it was created, in UTC. Left out when that is not known. Example: 2026-03-02T08:15:00Z.
eansarray of stringYesThe product's barcodes. A product can be linked to an article in a supplier's catalogue, and then that article's barcodes are included. Can be empty. Example: ["4064666230412"].
idstringYesProduct id. Example: prd_5Tg8mPq1Xc7Jd2Ls9WbNrK.
latestPurchasePricestringNoWhat one cost at the latest delivery, VAT excluded. Only with the scope stock:cost. Example: 62.4500.
locationIdstringYesExample: loc_4fK9xQ2mLr8TzB1vNc7HdW.
namestringYesExample: Color Touch 7/0.
notForSalebooleanYesMarked as not sold to customers, such as colour used in a treatment. It is a mark only: the point of sale does not refuse to sell the product. Example: true.
pricestringNoThe sale price, VAT included. Left out when none is set. Example: 149.00.
stockQuantityinteger (int32)NoThe stock balance. Never negative. Left out when the product is not stock-tracked. Example: 12.
stockTrackedbooleanYesWhether the location keeps a stock balance for the product. Example: true.
updatedAtstring (date-time)YesWhen it last changed, in UTC. Example: 2026-10-12T09:24:31.512Z.
vatPercentstringNoVAT rate in per cent. Example: 25.

ProductList

FieldTypeRequiredDescription
dataarray of ProductYes
nextCursorstringNoPass as after to fetch the next page. Absent on the last page. Example: prd_5Tg8mPq1Xc7Jd2Ls9WbNrK.

StockDocument

Stock changes that belong together, such as the rows of a delivery note or what one treatment used. Every line is applied, or none.

FieldTypeRequiredDescription
commentstringNoA note, at most 500 characters. Stored on every movement. Example: Colour for Anna, 14:00.
externalReferencestringNoYour own reference for the document, such as a delivery note number, 1 to 100 characters. A document whose reference has been used at the location changes nothing and is answered with the movements made the first time, so it is safe to send again at any time. Example: DN-2026-10412.
linesarray of StockMovementLineYes1 to 100 lines, applied in order.
staffIdstringNoThe staff member who did it. Shown as who made the movements. Example: stf_7Qm2xLd9Kc4Vb1Ts8HpWzE.

StockDocumentResult

FieldTypeRequiredDescription
dataarray of StockMovementYesThe movements made, in the order of the lines.

StockMovement

One change of the stock balance of a product or of a colour bar item. A movement is never changed or removed: a mistake is undone by a new movement that corrects it.

FieldTypeRequiredDescription
balanceAfterinteger (int32)YesThe balance after this movement. Example: 505.
colourBarItemIdstringNoThe colour bar item whose balance changed, in packs. Left out when the movement is a product's. Example: cbi_3Vn8Kq5TzL1xWc7PdM2RbJ.
colourBarMixIdstringNoThe colour bar mix that made the movement, by finishing a pack or by being undone. Such a movement is not corrected on its own: the mix is undone. Example: cbm_8Wq3Nd6HtY1mKx4LcP9RvB.
commentstringNoExample: Colour for Anna, 14:00.
correctedByMovementIdstringNoOn a movement that has been corrected: its correction. Example: mov_6Wr3tYu8Io1Pa4Sd7FgHjK.
correctsMovementIdstringNoOn a correction: the movement it undoes. Example: mov_9Bd2fHk4Mn7Pq1Sv6XzCtA.
createdAtstring (date-time)YesExample: 2026-10-11T09:24:31.512Z.
externalReferencestringNoThe caller's own reference for what the movement belongs to. Example: visit-88213.
idstringYesMovement id. Example: mov_3Lp6vRt9Yx2Kc5Ng8QwEzH.
listValuestringNoOn a withdrawal: what was taken, at the list price then of the article in a supplier's catalogue that it is linked to, VAT excluded, as a decimal string. Left out when there is no such list price. It is kept as it was: a later change of the list price does not change it. Example: 304.00.
productIdstringNoThe product whose balance changed. Left out when the movement is a colour bar item's. Example: prd_5Tg8mPq1Xc7Jd2Ls9WbNrK.
quantityinteger (int32)YesThe change applied to the balance: negative when stock left. Example: -2.
reasonstringNoWhy stock was taken out: a lower-case word such as consumption or damaged. The list is open, so accept a value you do not know. Example: consumption.
residueinteger (int32)NoOn a withdrawal made by a colour bar mix: the grams that were never taken from the pack it finished. Example: 3.
shortfallinteger (int32)YesHow much of what was asked for was not in stock; the balance never goes below 0. A sale of 3 with 1 in stock has quantity -1 and shortfall 2. 0 when everything was in stock. Example: 0.
staffIdstringNoWho made it, when a staff member did: look the name up in the staff list. Left out for a movement made without one, such as by an administrator in Cliento. Example: stf_7Qm2xLd9Kc4Vb1Ts8HpWzE.
typestringYessale and refund: at the point of sale. delivery: goods received, or the adjustment made when a delivery is changed afterwards. withdrawal: taken out by hand. correction: undoes the movement in correctsMovementId. stocktake: the difference found by a count. opening_balance: the balance when movements began to be recorded for the product. More values may be added. Example: withdrawal.

StockMovementLine

One line of a stock document.

FieldTypeRequiredDescription
colourBarItemIdstringNoThe colour bar item, in place of productId. Its quantity is in whole packs. Example: cbi_3Vn8Kq5TzL1xWc7PdM2RbJ.
movementIdstringNoFor a correction: the withdrawal to undo. Example: mov_9Bd2fHk4Mn7Pq1Sv6XzCtA.
productIdstringNoThe product. A delivery or withdrawal line names a product or a colour bar item, never both. Example: prd_5Tg8mPq1Xc7Jd2Ls9WbNrK.
quantityinteger (int32)NoHow many, as a whole number greater than 0. Required for all types but correction. Example: 2.
reasonstringNoFor a withdrawal: why, as one lower-case word such as consumption or damaged. The list is open. Example: consumption.
typestringYesdelivery: goods received. withdrawal: stock taken out. correction: undoes the withdrawal in movementId. Example: withdrawal.
unitCoststringNoFor a delivery: what one cost, VAT excluded, as a decimal string with at most four decimals. 0 when left out. Example: 62.4500.

StockMovementList

FieldTypeRequiredDescription
dataarray of StockMovementYes
nextCursorstringNoPass as after to fetch the next page. Absent when there is nothing more yet; to follow new movements, call again later with the id of the last movement you have read. Example: mov_3Lp6vRt9Yx2Kc5Ng8QwEzH.