Read stock


No V3 Stock API

Stock endpoints are part of the V1/V2 API. There are no V3 stock endpoints. Use the endpoints documented below.


Contents


Overview

This guide shows you how to read stock data from Xentral via the API. You will learn how to query stock levels, understand different stock types, find which warehouses and storage locations hold your products, and check reservations.

Use Cases:

  • Sync stock levels to your online shop (Shopify, WooCommerce, etc.)
  • Build a stock dashboard or reporting tool
  • Trigger low-stock alerts and automated reordering
  • Route orders to the correct warehouse based on availability
  • Audit batch, serial number, and best-before-date data at storage locations

This is a read-only guide. It covers querying stock data. For writing stock (goods receipt, stock movements, stocktaking), see the related resources at the end.

New to the Xentral API? Read first:


ERP Context

Important for developers without ERP experience: Stock in an ERP is not a single number. It is a set of different stock figures that reflect the physical reality, outstanding orders, and business rules.

Why "stock" is not just one number

In a typical shop system, you have one stock count per product. In Xentral, the same product can have:

  • 50 units physically in the warehouse (physical stock)
  • 10 units reserved for existing sales orders (reserved stock)
  • 40 units available to sell (sellable stock)
  • 15 units across open sales orders that have not shipped yet
  • A pseudo stock correction reported back to your shop

These numbers exist because Xentral manages the full order-to-shipment lifecycle. Stock changes happen at different points depending on your Xentral configuration -- not automatically when an order is placed.

What triggers stock changes?

EventStock Impact
Sales order createdStock can be reserved, depending on project settings (auto-reservation). Physical stock stays the same.
Delivery note processed (with auto shipping)Physical stock is reduced. Goods leave the warehouse. Without auto shipping, stock is not reduced automatically -- you need to trigger the stock booking manually or via the shipping process.
Goods receipt (purchase order)Physical stock increases.
Production completedFinished goods stock increases. Component stock decreases as a separate step (retrograde deduction). These are two distinct actions, not one trigger.
Return booked inPhysical stock increases.
Cycle count / Inventory (Inventur)Physical stock is adjusted to match the actual count. Can increase or decrease stock.
Transfer / Stock sync (3PL, Fulfillment)Stock is moved between warehouses or synced with external fulfillment providers.
Consignment warehouseGoods physically leave your warehouse (via delivery note) but remain in your stock -- they are booked into the customer's consignment warehouse.

Key insight: Stock reservation, stock booking, and stock deduction are all separate actions in Xentral. Which ones happen automatically depends on your project settings, logistics process configuration, and whether auto shipping is enabled.

Warehouse hierarchy

Xentral uses a two-level hierarchy:

  1. Warehouse (Lager) -- a room, building, or logical area
  2. Storage Location (Lagerplatz) -- a shelf, compartment, or pallet space within a warehouse

Every warehouse must have at least one storage location. Products are stored at specific storage locations, not just "in a warehouse". This matters when you need to know where exactly a product is -- for picking optimization, warehouse routing, or auditing.

Help Center: For general information about warehouse setup in Xentral, see Creating a warehouse structure.


Understanding the Stock Response

The main stock endpoint (GET /api/v1/products/\{id\}/stocks) returns three sections:

1. totals -- Aggregated stock across all warehouses

This gives you the overall picture for the product. All values are summed across all warehouses.

FieldDescription
physicalActual quantity physically present across all warehouses. This is the "real" count.
sellableQuantity available to commit to new orders. This is typically the number your shop should display.
reservedQuantity earmarked for existing sales orders. These units are spoken for but have not left the warehouse yet.
calculatedPhysical minus all open (unshipped) sales order quantities. Shows what will remain after all current orders ship.
openSalesOrdersTotal quantity across all unshipped sales orders for this product.
pseudoA manual stock override value. Can be null if not configured.
correctionA stock correction offset applied at the product level.
producibleQuantity that could be produced from available components (for BOM/production items). null if no bill of materials.

The core formula:

Sellable = Physical - Reserved

Warning for JIT (Just-in-Time) products: The totals section aggregates stock across all warehouses. For JIT products with components in different warehouses, the totals can be misleading. Example: a JIT product has two components -- one only in Warehouse A, the other only in Warehouse B. The totals will show stock, but you cannot actually sell the product if both components need to ship together from the same warehouse. For JIT products, always check the per-warehouse breakdown.

2. warehouses[] -- Physical stock per warehouse

Shows how stock is distributed across warehouses. Useful for multi-warehouse routing (e.g., ship from the warehouse closest to the customer).

Warehouse 1 (Berlin):  physical = 30
Warehouse 2 (Munich):  physical = 20
                        -------------
Total physical:                  50

3. salesChannels[] -- Per-channel stock adjustments

Lets Xentral report different stock levels to different shops. Each sales channel can have:

  • pseudo -- A fixed stock override for this channel. If set, report this value instead of sellable.
  • correction -- An offset added to the stock reported to this channel.

Note: Stock corrections (correction) exist at two levels: on the product itself (in totals.correction) and per sales channel (in salesChannels[].correction). If you use sales channel corrections, check both.


Endpoints Overview

What do you want to achieve?EndpointScope
"How much stock does product X have?"GET /api/v1/products/\{id\}/stocksproductStock:read
"Where is product X physically stored?"GET /api/v1/products/\{id\}/storageLocationsproduct.listWarehouseEntries
"What is reserved for product X and why?"GET /api/v1/products/\{id\}/reservationsNo scope required
"Which warehouses exist?"GET /api/v1/warehousesCheck instance settings
"What storage locations does warehouse Z have?"GET /api/v1/warehouses/\{id\}/storageLocationsstorageLocation:read
"What items are at a specific storage location (with batch/BBD/serial)?"GET /api/v2/warehouses/\{wId\}/storageLocations/\{sId\}/itemsstorageItem:read

Get Stock Totals for a Product

This is the most important endpoint for shop integrations. It returns all stock figures for a single product.

Required Scope: productStock:read

Request:

curl -s "https://{instance}.xentral.biz/api/v1/products/{productId}/stocks" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

API Reference: View Product Stocks

Response:

{
  "data": {
    "id": "4",
    "totals": {
      "pseudo": null,
      "sellable": 40.0,
      "reserved": 10.0,
      "correction": 0.0,
      "physical": 50.0,
      "openSalesOrders": 15.0,
      "calculated": 35.0,
      "producible": null
    },
    "warehouses": [
      {"id": "1", "physical": 30.0},
      {"id": "2", "physical": 20.0}
    ],
    "salesChannels": [
      {"id": "1", "pseudo": 10, "correction": -5}
    ]
  }
}

How to use this for shop sync:

  1. Read totals.sellable -- this is the available-to-promise quantity across all warehouses.
  2. If you use sales channel corrections, find your channel in salesChannels[] and apply the pseudo and correction values.
  3. If you need per-warehouse stock for routing decisions, iterate over warehouses[].

Note: If both pseudo and correction are null for a sales channel, the channel sees the same stock as totals.sellable.


Get Storage Locations for a Product

Shows which storage locations hold a specific product and how many units are at each location.

Required Scope: product.listWarehouseEntries

Request:

curl -s "https://{instance}.xentral.biz/api/v1/products/{productId}/storageLocations" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

API Reference: Product Storage Locations

Response:

{
  "data": [
    {
      "id": "17",
      "amount": "25.00",
      "product": {"id": "4"},
      "storageLocation": {
        "id": "1",
        "name": "Shelf-A01",
        "warehouse": {"id": "1", "name": "Main Warehouse"}
      }
    }
  ],
  "extra": {
    "page": {"number": 1, "size": 10}
  }
}

When to use this:

  • Warehouse operations: finding where to pick a product
  • Auditing: verifying that stock distribution matches expectations
  • Reporting: showing stock distribution across locations

Note: The amount here is the physical quantity at each location. It does not account for reservations. This endpoint does not include quality control attributes (batch, BBD, serial numbers) -- use the Items at a Storage Location endpoint for that level of detail.


Get Reservations for a Product

Shows which sales orders have claimed stock for a product. This helps you understand why sellable might be lower than physical.

No scope required for this endpoint.

Request:

curl -s "https://{instance}.xentral.biz/api/v1/products/{productId}/reservations" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

API Reference: Product Reservations

Response:

{
  "data": [
    {
      "id": "17",
      "amount": "25.00",
      "reason": "Sales order SO-2024-001",
      "project": {"id": "1"}
    }
  ]
}

When to use this:

  • Debugging: "Why does the product show 50 physical but only 25 sellable?"
  • Dashboard: Showing a breakdown of where stock is committed
  • Alerting: Detecting when reservations consume most of the available stock

Important: Reservations in Xentral are not on warehouse level. A reservation says "10 units are reserved for this order" but does not tell you which warehouse they will be picked from. This means you cannot calculate sellable stock per warehouse by subtracting reservations from per-warehouse physical stock.

Note: Whether reservations are created automatically when a sales order is placed depends on the project settings (auto-reservation). In some configurations, reservations must be created manually.


List Warehouses

Discover which warehouses exist in the Xentral instance. This gives you the warehouse IDs needed for subsequent calls.

Request:

curl -s "https://{instance}.xentral.biz/api/v1/warehouses" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

API Reference: List Warehouses

Response:

{
  "data": [
    {
      "id": "1",
      "designation": "Hauptlager",
      "project": null
    }
  ],
  "extra": {
    "page": {"number": 1, "size": 10},
    "totalCount": 1
  }
}

Key fields:

  • id -- The warehouse ID you will use in other endpoints.
  • designation -- Human-readable warehouse name.
  • project -- If set, this warehouse is associated with a specific project.

Note: Xentral has two different warehouse concepts: preferred warehouses (set on the sales order to prefer a specific warehouse for fulfillment) and project warehouses (exclusively assigned to a project). The project field on a warehouse indicates a project warehouse. The behavior of project warehouses with stock visibility depends on your instance configuration.

Filter by designation:

curl -s "https://{instance}.xentral.biz/api/v1/warehouses?filter[0][key]=designation&filter[0][op]=equals&filter[0][value]=Hauptlager" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

List Storage Locations in a Warehouse

Lists all storage locations within a specific warehouse. Use it to discover location IDs for the items endpoint or to understand a warehouse's layout.

Required Scope: storageLocation:read

Request:

curl -s "https://{instance}.xentral.biz/api/v1/warehouses/{warehouseId}/storageLocations" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

API Reference: List Storage Locations

Response:

{
  "data": [
    {
      "id": "1",
      "warehouse": {"id": "1"},
      "designation": "Lagerplatz1",
      "description": "",
      "project": null,
      "sort": 0,
      "isReplenishmentLocation": false,
      "isConsumptionLocation": false,
      "isRestrictedLocation": false,
      "productionAccess": false,
      "isPosLocation": false,
      "abcCategory": "",
      "dimensions": {"length": 0, "width": 0, "height": 0}
    }
  ],
  "extra": {
    "page": {"number": 1, "size": 10},
    "totalCount": 1
  }
}

Key fields:

  • id -- Storage location ID, needed for the items endpoint.
  • designation -- Human-readable location name.
  • sort -- Sorting number that defines the physical walking order for picking optimization.
  • isReplenishmentLocation, isConsumptionLocation, isRestrictedLocation, productionAccess, isPosLocation -- Boolean flags indicating the location type. See Storage Location Types.
  • dimensions -- Length, width, height for capacity planning.

Get Items at a Storage Location

The most detailed endpoint. Returns the actual items stored at a specific storage location, including batch numbers, best-before dates, and serial numbers. This is a V2 endpoint.

Required Scope: storageItem:read

Request:

curl -s "https://{instance}.xentral.biz/api/v2/warehouses/{warehouseId}/storageLocations/{storageLocationId}/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

API Reference: List Storage Location Items

Response:

{
  "data": [
    {
      "productId": "1",
      "sku": "ABC-12345-S-BL",
      "quantity": 5,
      "qualityControlAttributes": [
        {
          "quantity": 5,
          "batch": "Batch1",
          "bestBeforeDate": "2025-01-01",
          "serialNumbers": [
            {"number": "001"}
          ]
        }
      ]
    }
  ],
  "extra": {
    "totalCount": 1,
    "page": {"number": 1, "size": 10}
  }
}

Key fields:

  • productId -- The product at this location.
  • sku -- The product's SKU / article number.
  • quantity -- Total quantity of this product at this location.
  • qualityControlAttributes[] -- Detailed breakdown by batch, BBD, and serial numbers.

When to use this -- specifically for quality control attributes:

  • Food/pharma: Checking expiration dates for FIFO picking
  • Regulated products: Auditing batch traceability
  • Electronics/high-value goods: Verifying serial number inventory

Important: You need both the warehouse ID and storage location ID to call this endpoint. Use List Warehouses and List Storage Locations to discover these IDs first. Be aware that querying quality control attributes requires calling this endpoint for every storage location individually -- this can result in many API calls for large warehouses. Consider whether you actually need this level of detail before building a polling strategy around it.


Common Integration Patterns

Pattern 1: Stock Sync to Shop

The most common use case. Periodically sync stock levels from Xentral to your online shop.

+----------+  Poll periodically  +----------+  Update stock  +----------+
|  Xentral | ------------------> |   Your   | -------------->|   Shop   |
|   API    |  GET products/      |Middleware|                |  (e.g.   |
|          |  {id}/stocks        |          |                | Shopify) |
+----------+                     +----------+                +----------+

Recommended approach:

  1. Maintain a list of product IDs to sync (your product catalog mapping).
  2. For each product, call GET /api/v1/products/\{id\}/stocks.
  3. Use totals.sellable as the available quantity.
  4. If using sales channel corrections, check salesChannels[] for your channel's pseudo/correction.
  5. Update your shop's stock level.

Best Practice: For large catalogs, batch your API calls and respect rate limits. Process the most critical products (best sellers, low stock) first.

Pattern 2: Low-Stock Alerting

Monitor stock levels and trigger alerts when stock falls below a threshold.

What to monitor:

  • totals.sellable dropping below a minimum threshold
  • totals.sellable approaching zero while totals.openSalesOrders is high (demand exceeding supply)
  • totals.reserved / totals.physical ratio exceeding 80% (most stock is committed)

Pattern 3: Multi-Warehouse Order Routing

When you have multiple warehouses, route orders to the warehouse that has stock.

# Get stock with per-warehouse breakdown
curl -s "https://{instance}.xentral.biz/api/v1/products/{productId}/stocks" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

# Check warehouses[] array for physical stock per warehouse
# Route order to warehouse with highest stock or closest to customer

Important: The warehouses[] array only shows physical stock per warehouse -- not sellable. Since reservations in Xentral are not tracked on warehouse level, you cannot calculate per-warehouse sellable stock. For most routing decisions, physical stock per warehouse is sufficient.

Pattern 4: Batch/BBD Compliance Check

For food, pharmaceuticals, or regulated products, verify that items in stock are within their best-before dates.

# 1. Find storage locations for a product
curl -s "https://{instance}.xentral.biz/api/v1/products/{productId}/storageLocations" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

# 2. For each storage location, get item details with BBD
curl -s "https://{instance}.xentral.biz/api/v2/warehouses/{warehouseId}/storageLocations/{storageLocationId}/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

# 3. Check qualityControlAttributes[].bestBeforeDate for expiration

Storage Location Types

Storage location types affect which stock is available for order fulfillment.

TypeAPI FlagDescription
NormalAll flags falseStandard storage. Used for all operations.
ReplenishmentisReplenishmentLocation: trueSafety stock / buffer. Products must be relocated to a normal location before they can be picked.
ConsumptionisConsumptionLocation: trueProducts booked here are removed from stock entirely. Used for scrapping or internal consumption.
Quarantine / BlockedisRestrictedLocation: trueStock is invisible to logistics. Used for defective returns, locked products, or non-sale items.
ProductionproductionAccess: trueRestricts warehouse to production use. Stock is used for manufacturing, not order fulfillment.
POSisPosLocation: trueUsed with Xentral POS system. Online logistics does not access this stock.

Note: Whether these storage location type settings are fully reflected in the stock figures returned by GET /products/\{id\}/stocks should be verified for your specific use case. If your stock sync shows unexpected numbers, check whether products are stored in replenishment, quarantine, or POS locations.


Polling and Rate Limits

Polling Strategy

Xentral does not currently have webhook events that give a complete picture of all stock changes. Some events exist (e.g., storage-item-movement, reservation-changed) but they do not cover all scenarios. In practice, you need to poll.

StrategyWhen to useTrade-off
Poll all products periodicallySmall catalog (fewer than 1,000 products)Simple but uses many API calls. Poll every 5-15 minutes.
Poll only changed productsLarge catalog, you track which products had orders/shipmentsFewer API calls but requires order event tracking on your side.
Full sync on scheduleNightly reconciliationReliable baseline. Combine with frequent polling for critical products.

Warning: Stock synchronization with large product catalogs will hit rate limits. If you have thousands of products, you cannot poll all of them every few minutes. Prioritize high-turnover products and use longer intervals for slow-moving items.

Rate Limiting

Monitor the X-RateLimit-Remaining header. Rate limits vary by customer instance.

RemainingRecommended Action
Above 50%Full speed (no delay)
Below 50%Add a small delay (e.g., 50ms) between requests
Below 25%Add a larger delay (e.g., 200ms) between requests

Tip: Implement exponential backoff on 429 responses. Wait for the time indicated in the Retry-After header before retrying.

Caching Recommendations

DataCache?Reason
Warehouse listYes, refresh dailyWarehouses rarely change.
Storage location listYes, refresh dailyStorage locations rarely change.
Stock totalsCache briefly (1-5 min)Stock changes with every order and shipment.
Quality control attributes (batch/BBD/serial)Decide per use caseQuerying this data requires calling the items endpoint for every storage location individually, which is expensive in API calls. Only poll this if you actually need batch/serial level detail.

Error Handling

StatusMeaningCommon Cause
400Bad RequestInvalid query parameters or filter syntax
401UnauthorizedInvalid or expired token
403ForbiddenMissing required scope (e.g., productStock:read)
404Not FoundProduct ID, warehouse ID, or storage location ID does not exist
406Not AcceptableMissing Accept: application/json header
429Too Many RequestsRate limit exceeded

Note: Xentral returns 400 Bad Request for validation errors, not 422. The Accept: application/json header is mandatory on every request -- without it you get a 406 error.


Pagination

All list endpoints support page-based pagination.

# First page, 50 items per page
curl -s "https://{instance}.xentral.biz/api/v1/warehouses?page[number]=1&page[size]=50" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json"

Response pagination metadata:

{
  "data": ["..."],
  "extra": {
    "page": {"number": 1, "size": 10},
    "totalCount": 47
  }
}

Use totalCount to determine how many pages you need: ceil(totalCount / page[size]).


Related Resources

API Documentation

Help Center

Related Guides