Stock levels sync
Read stock quantities per warehouse and variant, and keep the availability in an e-shop current.
Goal: show correct availability in an e-shop, or feed stock numbers into another system.
In WinShop, stock is kept as stock items. A stock item is the holding of one product, or one variant of a product, in one warehouse. A variant is one combination of colour, size, length and dimension. So the stock of a T-shirt is not one number: it is one number per variant and per warehouse.
You will use:
GET /api/item-lists/warehousesto pick the warehousesPOST /api/stock-items/filter-paginatedto read the quantities
Before you start
Ask the customer which warehouses count for your purpose. A company often has a central warehouse, shops, and warehouses for returns or claims. An e-shop usually sells from one or two of them. Keep the warehouse ids in your configuration.
Setup
The steps use one small helper for authenticated JSON calls. In C#, keep the record declarations of all snippets at the end of your file. GetAccessTokenAsync() and getAccessToken() are the token cache from Authentication.
using System.Net.Http.Headers;
using System.Net.Http.Json;
var http = new HttpClient { BaseAddress = new Uri("https://<your-instance>") };
async Task<T> SendAsync<T>(HttpMethod method, string path, object? body = null)
{
using var request = new HttpRequestMessage(method, path);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await GetAccessTokenAsync());
if (body is not null) request.Content = JsonContent.Create(body);
using var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
return (await response.Content.ReadFromJsonAsync<T>())!;
}
record Page<T>(List<T> Data, int CurrentPage, int TotalPages, int TotalCount, int PageSize,
bool HasPreviousPage, bool HasNextPage);The sync, step by step
Find the warehouse ids
Warehouses are an item list. Read it once and let the customer choose.
curl https://<your-instance>/api/item-lists/warehouses \
-H "Authorization: Bearer $ACCESS_TOKEN"[
{ "id": 1, "number": "01", "name": "Central warehouse" },
{ "id": 2, "number": "02", "name": "Shop Prague" }
]Read the stock items of those warehouses
Search the stock items with the warehouse ids in filter.warehouseId. Ask only for the identifying fields and the quantities, and page through. See Filtering and pagination for the request shape.
curl -X POST https://<your-instance>/api/stock-items/filter-paginated \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filter": { "warehouseId": [1, 2] },
"pageNumber": 1,
"pageSize": 1000,
"orderBy": ["id"],
"limitFields": [
"id", "productId", "warehouseId", "colorId", "sizeId", "lengthId", "dimensionId",
"quantityInStock", "quantityBlocked", "quantityAvailable"
]
}'One row of the response:
{
"id": 48213,
"productId": 5120,
"warehouseId": 1,
"colorId": 4,
"sizeId": 12,
"lengthId": 0,
"dimensionId": 0,
"quantityInStock": 14,
"quantityBlocked": 3,
"quantityAvailable": 11
}The stock filter also accepts the product criteria of the product search under filter.basic, for example groupId or productId, if you only need part of the assortment.
Understand the quantities
| Field | Meaning |
|---|---|
quantityInStock | The stock level WinShop has on record for the item ("In stock" in the WinShop application). |
quantityBlocked | The quantity WinShop reports as blocked. The drill-down endpoints below show what blocks it: orders, reservations and other documents. |
quantityAvailable | The quantity WinShop reports as available ("Available quantity" in the application). This is normally the number an e-shop shows; confirm it with the customer. |
quantityOrdered, quantityOrderedAllWarehouses | The ordered quantity for this warehouse and across all warehouses. Not part of what you can sell now. |
minQuantity, maxQuantity, optQuantity | The minimum, maximum and optimal stock level configured for the item. Planning values, not stock. |
Use quantityAvailable as WinShop gives it to you. The API does not document how it is derived from the other fields, so do not calculate it yourself.
Quantities are decimals, because goods can be sold by weight or length.
To see what exactly blocks the stock of one item, the API has drill-down endpoints such as blocked goods in e-shop orders.
Aggregate and store
An e-shop usually wants one number per sellable thing: per variant, summed over the warehouses it sells from.
var stock = await LoadStockAsync();
var availability = stock
.GroupBy(row => (row.ProductId, row.ColorId, row.SizeId, row.LengthId, row.DimensionId))
.ToDictionary(group => group.Key, group => group.Sum(row => row.QuantityAvailable));
foreach (var (variant, quantity) in availability)
{
// never publish a negative number
var sellable = Math.Max(0, quantity);
// update variant.ProductId / ColorId / SizeId in your system
}A product or variant that has no stock item in your warehouses does not appear in the result at all. Treat "not in the result" as zero.
How often to poll
Stock changes all day: every sale at a cash register, every processed delivery note and every intake moves it. Polling is always a compromise between freshness and load.
- Run the full read every 10 to 15 minutes as a baseline. With
limitFieldsand a page size around 1000 it is a modest load even for a large assortment. - Do not overlap runs. Start the next run only after the previous one finished.
- Do not poll single products in a loop. One paged search is far cheaper than thousands of single requests.
- Re-check at the critical moment. Stock from a poll can be minutes old. If overselling is costly, check the items of a basket again at checkout. For a few products, the variant matrix for one warehouse returns the current
quantityInStockandquantityBlockedper variant in one call.
Why webhooks are better, when you can get them
With polling you read the whole stock to find the few rows that changed, and you are still late by up to one interval. Webhooks turn that around: WinShop tells you when something changes, and you update just that item within seconds.
Webhooks are set up by WinShop support on request. If the customer can have them, use webhooks for freshness and keep a slow full read, for example nightly, as the safety net that corrects anything that was missed.