Update 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 change stock levels in Xentral via the API. You will learn how to add stock (goods receipt), remove stock (retrieval), overwrite stock (set total), and handle quality control attributes like batches, best-before dates, and serial numbers.

Use Cases:

  • Sync stock from an external warehouse management system (WMS) or 3PL provider into Xentral
  • Book in goods receipts when purchase orders arrive
  • Remove stock for manual corrections (damaged goods, shrinkage)
  • Set absolute stock levels during inventory synchronization or initial import
  • Book in returned goods from customer returns
  • Maintain batch and serial number traceability
  • Move stock between storage locations from an external warehouse app

Companion Guide: This guide covers writing stock data. For reading stock levels, warehouse structures, and storage locations, see Read Stock.

New to the Xentral API? Read first:


ERP Context: How Stock Changes Work in Xentral

Important for developers without ERP experience: Stock in an ERP is not a simple counter you increment and decrement. Every stock change is a documented business event with specific triggers, audit trails, and downstream effects.

Why stock changes are not simple writes

In a shop system, you might just set stock = 50 and be done. In Xentral, stock changes are tied to business processes:

  • Goods receipt (Wareneingang): Products arrive from a supplier. Stock increases. This is typically linked to a purchase order.
  • Stock retrieval (Auslagerung): Products leave the warehouse. This happens when delivery notes are processed, or via manual correction.
  • Set total stock (Bestandskorrektur): Overwrite the current stock with a new value. Used for inventory synchronization with external systems or after physical stocktaking.
  • Returns (Retouren): Customers send products back. Stock increases after the return is processed and booked in.

What happens when stock changes?

ActionStock ImpactTriggered By
Add items to storage locationPhysical stock increases at a specific locationManual stock-in, WMS sync, initial import
Retrieve items from storage locationPhysical stock decreases at a specific locationManual correction, shrinkage, damage write-off
Set total stockStock is overwritten to match the provided value3PL sync, inventory count, system reconciliation
Goods receipt (purchase order)Physical stock increases, linked to a purchase orderSupplier delivery arrives
Goods receipt (return)Physical stock increases, linked to a returnCustomer return booked in
Delivery note processedPhysical stock decreases (outbound)Shipping process -- not covered in this guide

Key concept: Storage location level

All stock write operations in Xentral happen at the storage location level, not at the warehouse level. You always need to specify which shelf/bin (storage location) within which warehouse the stock change applies to.

Warehouse "Main Warehouse" (ID: 1)
  +-- Storage Location "Shelf-A01" (ID: 1)  <- Stock changes happen HERE
  +-- Storage Location "Shelf-B02" (ID: 2)

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

Key concept: SKU vs. Product ID

The stock write endpoints (stockItem and retrieveItem) use the product SKU (article number) to identify products -- not the product ID. This is different from most other Xentral API endpoints which use IDs. The setTotalStock endpoint uses the product ID.


Endpoints Overview

What do you want to do?EndpointMethodScope
Add stock to a storage locationPOST /api/v1/warehouses/\{wId\}/storageLocations/\{sId\}/itemsPOSTstorageItem:update
Remove stock from a storage locationPATCH /api/v1/warehouses/\{wId\}/storageLocations/\{sId\}/itemsPATCHstorageItem:update
Overwrite total stock at storage locationsPATCH /api/v1/storageLocations/setTotalStockPATCHNo scope required
Book in goods from a purchase orderPOST /api/v1/purchaseOrders/\{id\}/goodsReceiptsPOSTgoodsReceipt:create
Book in goods from a returnPOST /api/v1/returns/\{id\}/goodsReceiptsPOSTgoodsReceipt:create

Prerequisites

  • Xentral account with API access
  • Personal Access Token (PAT) with required scopes:
    • storageItem:update - For direct stock-in and stock-out
    • goodsReceipt:create - For goods receipts from purchase orders and returns
    • No scope required for setTotalStock
  • At least one warehouse with at least one storage location configured in Xentral
  • Products must exist in the system before stock can be booked
  • Products must be flagged as stock items (Lagerartikel) in their master data
  • Product SKUs must be unique across all products (the stock-in/stock-out endpoints identify products by SKU)

Finding warehouse and storage location IDs: Use the read endpoints documented in G4: Read Stock:


Before You Start

Decision 1: Which stock update method?

Choose the right endpoint based on your use case:

Option A: Stock In / Stock Out (incremental)

  • Use POST .../items to add a quantity and PATCH .../items to remove a quantity
  • Best for: Individual goods receipts, manual corrections, event-driven updates
  • Stock changes are relative: "add 10 units" or "remove 3 units"
  • Each call creates a stock movement record (auditable)

Option B: Set Total Stock (absolute overwrite)

  • Use PATCH /api/v1/storageLocations/setTotalStock
  • Best for: Full inventory sync from an external WMS/3PL, initial stock import, post-stocktaking reconciliation
  • Stock changes are absolute: "this location now has exactly 50 units"
  • Warning: Any stock not specified in the payload is removed. This includes quality control attributes (batch, BBD, serial numbers).

Option C: Goods Receipt (document-linked)

  • Use POST /api/v1/purchaseOrders/\{id\}/goodsReceipts or POST /api/v1/returns/\{id\}/goodsReceipts
  • Best for: Receiving goods against existing purchase orders or returns
  • Stock change is linked to a document -- enables traceability back to the purchase order or return
  • Supports splitting deliveries across multiple warehouses and storage locations
CriteriaStock In/OutSet Total StockGoods Receipt
Change typeRelative (+/-)Absolute (overwrite)Relative (+)
Identifies product bySKUProduct IDProduct ID
Linked to documentNoNoYes (PO/Return)
Audit trailStock movementOverwrite eventFull document chain
Best forCorrections, manualWMS sync, importPurchase orders, returns

Decision 2: Do you need batch/serial number tracking?

If your products use batch numbers, best-before dates, or serial numbers, you must include the qualityControlAttributes (with batch/bestBeforeDate/serialNumbers fields, depending on the endpoint) in your stock write requests. Xentral will reject the stock change if:

  • A product is configured for batch tracking and you do not provide a batch number
  • A product is configured for serial number tracking and you do not provide serial numbers matching the quantity

Help Center: For information about batch and BBD management, see Best before date (BBD).

Decision 3: Single location or multiple locations?

For the setTotalStock endpoint, you can update multiple storage locations in a single API call. For stockItem and retrieveItem, each call targets one specific storage location. Plan your integration accordingly:

  • Few locations: Individual calls per location are fine
  • Many locations: Use setTotalStock but send one request per storage location with up to 10--15 products each to avoid timeouts

Stock In: Add Items to a Storage Location

Adds stock for a product to a specific storage location. This is the most common way to manually increase stock.

Required Scope: storageItem:update

Important: This endpoint identifies products by SKU (article number), not by product ID.

Request:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/warehouses/{warehouseId}/storageLocations/{storageLocationId}/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "sku": "100001"
    },
    "quantity": 25
  }'

API Reference: Add Item to Storage Location

Response: 201 Created

The stock is immediately increased at the specified storage location. No response body is returned.

With a reason for the stock movement:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/warehouses/1/storageLocations/1/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "sku": "100001"
    },
    "quantity": 25,
    "reason": "Initial stock import from external WMS"
  }'

With batch and best-before date:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/warehouses/1/storageLocations/1/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "sku": "100001"
    },
    "quantity": 50,
    "batch": "LOT-2026-001",
    "bestBeforeDate": "2027-06-30"
  }'

With serial numbers:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/warehouses/1/storageLocations/1/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "sku": "100001"
    },
    "quantity": 3,
    "serialNumbers": [
      {"number": "SN-2026-001"},
      {"number": "SN-2026-002"},
      {"number": "SN-2026-003"}
    ]
  }'

Note: Whether serial numbers are required depends on the serial number tracking mode configured on the product. In some modes (e.g., "Originals use"), serial numbers are only required at the delivery note stage, not during stock-in. If the product's serial number mode requires serial numbers at stock-in, the count must match the quantity.


Stock Out: Retrieve Items from a Storage Location

Removes stock for a product from a specific storage location. Use this for manual corrections, damage write-offs, or shrinkage.

Required Scope: storageItem:update

Important: This endpoint also uses SKU, not product ID. The HTTP method is PATCH, not DELETE.

Request:

curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/warehouses/{warehouseId}/storageLocations/{storageLocationId}/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "sku": "100001"
    },
    "quantity": 5
  }'

API Reference: Retrieve Item from Storage Location

Response: 204 No Content

The stock is immediately reduced at the specified storage location.

With reason and batch:

curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/warehouses/1/storageLocations/1/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "sku": "100001"
    },
    "quantity": 3,
    "batch": "LOT-2026-001",
    "reason": "Damaged during warehouse inspection"
  }'

With serial numbers:

curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/warehouses/1/storageLocations/1/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {
      "sku": "100001"
    },
    "quantity": 2,
    "serialNumbers": [
      {"number": "SN-2026-001"},
      {"number": "SN-2026-002"}
    ]
  }'

Warning: You cannot retrieve more stock than is physically present at the storage location. Attempting to do so will return a 400 Bad Request error.

Note: When retrieving items with batch, BBD, or serial numbers, those quality control attributes must actually have stock at the location. You cannot remove QCA entries that have zero stock.


Set Total Stock (Overwrite)

This endpoint overwrites the total stock at one or more storage locations. It is the most powerful stock write endpoint, but also the most dangerous.

No scope required. This endpoint does not require a specific API scope.

Warning: Any stock that was previously present at the storage location and is not specified in the payload will be removed entirely. This includes all quality control attributes (batch, BBD, serial numbers). If a product exists at a location and you do not include it in your payload, its stock becomes zero. Always include the complete stock picture for each storage location you update.

Important: This endpoint identifies products by product ID, not by SKU. This is different from the stockItem/retrieveItem endpoints.

Performance Warning: Large payloads can cause timeouts. Send one request per storage location and limit each request to approximately 10--15 products. If you need to update many locations, send multiple sequential requests.

Request -- Set stock for a single product at a single location:

curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/storageLocations/setTotalStock" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "data": [
      {
        "storageLocation": {
          "id": "1"
        },
        "totalStock": [
          {
            "product": {
              "id": "4"
            },
            "quantity": 100
          }
        ]
      }
    ]
  }'

API Reference: Set Total Stock on Storage Locations

Response: 204 No Content

Multiple products at one location:

curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/storageLocations/setTotalStock" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "data": [
      {
        "storageLocation": {
          "id": "1"
        },
        "totalStock": [
          {
            "product": {"id": "4"},
            "quantity": 100
          },
          {
            "product": {"id": "3"},
            "quantity": 50
          }
        ]
      }
    ]
  }'

Multiple storage locations in one call:

curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/storageLocations/setTotalStock" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "data": [
      {
        "storageLocation": {"id": "1"},
        "totalStock": [
          {"product": {"id": "4"}, "quantity": 60}
        ]
      },
      {
        "storageLocation": {"id": "2"},
        "totalStock": [
          {"product": {"id": "4"}, "quantity": 40}
        ]
      }
    ]
  }'

With quality control attributes:

curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/storageLocations/setTotalStock" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "data": [
      {
        "storageLocation": {"id": "1"},
        "totalStock": [
          {
            "product": {"id": "4"},
            "quantity": 50,
            "qualityControlAttributes": {
              "batch": "LOT-2026-001",
              "bestBeforeDate": "2027-12-31",
              "serialNumbers": [
                {"number": "SN-001"},
                {"number": "SN-002"}
              ]
            }
          }
        ]
      }
    ]
  }'

How setTotalStock works -- a detailed example

Suppose storage location "Shelf-A01" currently has:

  • Product 4: 30 units (Batch LOT-A)
  • Product 4: 20 units (Batch LOT-B)
  • Product 7: 10 units

You call setTotalStock with:

{
  "data": [{
    "storageLocation": {"id": "1"},
    "totalStock": [
      {"product": {"id": "4"}, "quantity": 50, "qualityControlAttributes": {"batch": "LOT-C"}}
    ]
  }]
}

Result:

  • Product 4: Now 50 units with Batch LOT-C (LOT-A and LOT-B are gone)
  • Product 7: Removed from this location (was not in the payload)

This is why setTotalStock is called a "total" stock set -- it replaces everything.


Goods Receipt from Purchase Order

When a supplier delivers goods against a purchase order, use this endpoint to book them into stock. This creates a traceable link between the purchase order and the stock increase.

Required Scope: goodsReceipt:create

Prerequisite: A purchase order must exist in Xentral before you can create a goods receipt for it. You can only book goods against existing positions on the purchase order, and the product must match the product on the position.

Request:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/purchaseOrders/{purchaseOrderId}/goodsReceipts" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-02-25",
    "positions": [
      {
        "product": {"id": "4"},
        "quantity": 100,
        "purchaseOrderPosition": {"id": "1"},
        "stockMovements": [
          {
            "quantity": 100,
            "warehouse": {"id": "1"},
            "storageLocation": {"id": "1"}
          }
        ]
      }
    ]
  }'

API Reference: Create Goods Receipt for Purchase Order

Response: 201 Created -- The goods receipt ID is in the Location header.

Split delivery across warehouses:

If goods need to be stored in multiple locations:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/purchaseOrders/{purchaseOrderId}/goodsReceipts" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-02-25",
    "positions": [
      {
        "product": {"id": "4"},
        "quantity": 100,
        "purchaseOrderPosition": {"id": "1"},
        "stockMovements": [
          {
            "quantity": 60,
            "warehouse": {"id": "1"},
            "storageLocation": {"id": "1"}
          },
          {
            "quantity": 40,
            "warehouse": {"id": "1"},
            "storageLocation": {"id": "2"}
          }
        ]
      }
    ]
  }'

Note: The sum of stockMovements[].quantity should match the position quantity. If it does not, the remaining quantity is considered "not yet received" and can be booked in via a subsequent goods receipt (partial delivery).

With quality control attributes:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/purchaseOrders/{purchaseOrderId}/goodsReceipts" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-02-25",
    "positions": [
      {
        "product": {"id": "4"},
        "quantity": 10,
        "purchaseOrderPosition": {"id": "1"},
        "stockMovements": [
          {
            "quantity": 10,
            "warehouse": {"id": "1"},
            "storageLocation": {"id": "1"},
            "qualityControlAttributes": {
              "batch": "BATCH-2026-001",
              "bestBeforeDate": "2027-12-31",
              "serialNumbers": [
                {"number": "SN-001"},
                {"number": "SN-002"}
              ]
            }
          }
        ]
      }
    ]
  }'

Goods Receipt from Return

When a customer returns products, use this endpoint to book them back into stock. This creates a traceable link between the return and the stock increase.

Required Scope: goodsReceipt:create

Prerequisite: A return must exist in Xentral before you can create a goods receipt for it. You can only book goods against existing positions on the return, and the product must match the product on the position.

Request:

curl -s -X POST "https://{instance}.xentral.biz/api/v1/returns/{returnId}/goodsReceipts" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-02-25",
    "positions": [
      {
        "product": {"id": "4"},
        "quantity": 2,
        "returnPosition": {"id": "1"},
        "stockMovements": [
          {
            "quantity": 2,
            "warehouse": {"id": "1"},
            "storageLocation": {"id": "1"}
          }
        ]
      }
    ]
  }'

API Reference: Create Goods Receipt for Return

Response: 201 Created -- The goods receipt ID is in the Location header.

The structure is identical to purchase order goods receipts, except you reference returnPosition instead of purchaseOrderPosition.

Tip: Consider booking returns into a quarantine/blocked storage location first for quality inspection, then relocating good items to normal storage. See Storage Location Types in G4.


Quality Control Attributes (Batch, BBD, Serial Numbers)

Quality control attributes are used across all stock write endpoints but with slightly different field names depending on the endpoint.

Field name differences by endpoint

EndpointBatch fieldBBD fieldSerial numbers field
stockItem (POST ...items)batchbestBeforeDateserialNumbers
retrieveItem (PATCH ...items)batchbestBeforeDateserialNumbers
setTotalStockqualityControlAttributes.batchqualityControlAttributes.bestBeforeDatequalityControlAttributes.serialNumbers
goodsReceipts (PO/Return)qualityControlAttributes.batchqualityControlAttributes.bestBeforeDatequalityControlAttributes.serialNumbers

When are quality control attributes required?

Whether you must provide batch/BBD/serial number data depends on the product's master data configuration:

  • Batch tracking enabled on product: You must provide a batch value when booking stock in or out for this product
  • BBD tracking enabled on product: You must provide a bestBeforeDate when booking stock in
  • Serial number tracking enabled on product: Depends on the tracking mode configured on the product. Some modes (e.g., "Generate own" or "Store and use originals") require serial numbers at stock-in with a count matching the quantity. Other modes (e.g., "Use originals") only require serial numbers at the delivery note stage, not during stock-in.

If a product requires these attributes and you omit them, the API will return a 400 Bad Request error.

Help Center: Best before date (BBD)


Common Integration Patterns

Pattern 1: Initial Stock Import

When setting up a new Xentral instance, import existing stock from your current system.

+--------------+     +---------------+     +--------------+
|  Current     |     |  Your         |     |   Xentral    |
|  WMS/ERP     | --> |  Migration    | --> |   API        |
|              |     |  Script       |     |              |
+--------------+     +---------------+     +--------------+

Recommended approach:

  1. Export current stock per storage location from your existing system
  2. Map product numbers to Xentral product IDs
  3. Map warehouse/shelf names to Xentral warehouse/storage location IDs
  4. Use PATCH /api/v1/storageLocations/setTotalStock to set stock per location
  5. Verify by reading stock back with GET /api/v1/products/\{id\}/stocks

Tip: Send one request per storage location with up to 10--15 products each. Respect rate limits.

Pattern 2: Ongoing 3PL / WMS Stock Sync

Periodically synchronize stock levels between an external warehouse system and Xentral.

Two approaches:

A) Full sync (setTotalStock):

  • Periodically read full stock from WMS
  • Use setTotalStock to overwrite Xentral stock
  • Simple but destructive -- ensure your WMS is the source of truth
  • Best for: External fulfillment providers (3PL) where Xentral does not manage warehouse operations

B) Event-based sync (stockItem / retrieveItem):

  • Listen for stock change events from your WMS
  • For each inbound event: POST .../items (add stock)
  • For each outbound event: PATCH .../items (remove stock)
  • More complex but preserves Xentral stock history
  • Best for: Hybrid setups where both systems manage stock

Pattern 3: Purchase Order Goods Receipt Flow

The standard flow for receiving goods from suppliers:

+--------------+     +---------------+     +--------------+     +--------------+
| 1. PO exists | --> | 2. Goods      | --> | 3. Stock     | --> | 4. Verify    |
| in Xentral   |     | arrive        |     | booked in    |     | stock levels |
+--------------+     +---------------+     +--------------+     +--------------+
  1. A purchase order exists in Xentral (created via API or UI)
  2. Goods physically arrive at the warehouse
  3. Call POST /api/v1/purchaseOrders/\{id\}/goodsReceipts with quantities and storage locations
  4. Verify stock increase with GET /api/v1/products/\{id\}/stocks

Partial deliveries: You can create multiple goods receipts for the same purchase order. Each goods receipt books in only the quantity you specify. The purchase order tracks how much has been received vs. ordered.

Pattern 4: Return Processing

+--------------+     +---------------+     +--------------+     +--------------+
| 1. Return    | --> | 2. Inspect    | --> | 3. Book into | --> | 4. Move to   |
| received     |     | quality       |     | quarantine   |     | normal stock |
+--------------+     +---------------+     +--------------+     +--------------+
  1. A return exists in Xentral
  2. Inspect the returned item for quality
  3. Book into a quarantine/blocked storage location using POST /api/v1/returns/\{id\}/goodsReceipts
  4. After inspection, relocate good items to normal storage (via UI or separate stock-in/stock-out calls)

Pattern 5: Stock Correction

For ad-hoc adjustments (damage, shrinkage, found items):

# Found 5 extra units during physical count
curl -s -X POST "https://{instance}.xentral.biz/api/v1/warehouses/1/storageLocations/1/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {"sku": "100001"},
    "quantity": 5,
    "reason": "Physical count correction - 5 extra units found"
  }'

# 3 units damaged and need to be written off
curl -s -X PATCH "https://{instance}.xentral.biz/api/v1/warehouses/1/storageLocations/1/items" \
  -H "Authorization: Bearer {token}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "product": {"sku": "100001"},
    "quantity": 3,
    "reason": "Damage write-off - water damage during transport"
  }'

Best Practice: Always provide a reason for stock corrections. This creates an audit trail that helps with investigations and accounting.


Error Handling

StatusMeaningCommon Cause
400Bad RequestInvalid JSON, missing required field, product is not a stock item, quantity exceeds available stock (for retrieval), batch/serial required but not provided
401UnauthorizedInvalid or expired token
403ForbiddenMissing required scope (e.g., storageItem:update)
404Not FoundSKU not found, warehouse ID, storage location ID, purchase order ID, or product ID does not exist
406Not AcceptableMissing Accept: application/json header
415Unsupported Media TypeMissing or incorrect Content-Type: application/json header
429Too Many RequestsRate limit exceeded

Note: Xentral returns 400 Bad Request for validation errors, not 422.

Common error scenarios

Product not found (wrong SKU) -- returns 404:

The stockItem and retrieveItem endpoints return 404 Not Found with no response body when the SKU does not exist. Verify the SKU matches the product's number field.

Product is not a stock item -- returns 400:

{
  "type": "https://api.xentral.biz/problems/generic-validation",
  "title": "Generic request validation failed.",
  "messages": ["Product must be a stock item"]
}

The product must have isStockItem: true in its master data. Check the product configuration in Xentral.

Product is not a stock item (setTotalStock) -- returns 400:

{
  "type": "https://api.xentral.biz/problems/generic-validation",
  "title": "Generic request validation failed.",
  "messages": ["product(s) with id(s): 4 are not stock items"]
}

Insufficient stock for retrieval -- returns 400:

{
  "type": "https://api.xentral.biz/problems/generic-validation",
  "title": "Generic request validation failed.",
  "messages": ["Item is out of stock"]
}

Batch/BBD not enabled on product -- returns 400:

{
  "type": "https://api.xentral.biz/problems/generic-validation",
  "title": "Generic request validation failed.",
  "messages": ["Batch option is not enabled on product with id 1", "BestBeforeDate option is not enabled on product with id 1"]
}

Missing scope:

{
  "message": "Missing required scopes: storageItem:update."
}

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. For large stock imports, use setTotalStock to batch multiple updates into fewer API calls.


Related Resources

API Documentation

Help Center

Related Guides