Applies to: Inventory API
Learn about the inventory process flow and how the Inventory API works.
Square tracks inventory by adding up all adjustments since the last physical count. If a seller has never done a physical count, Square starts from zero.
Because inventory adjustments and physical counts can arrive in any order (like when processing offline sales), each update needs a client-generated RFC 3339 timestamp. This helps Square put all changes in the right sequence. For example, an item variation starts with 100 units. Updates might come from both offline POS sales and your application's API calls. The RFC 3339 timestamp includes time zone information that's taken into account when Square determines the order of inventory transactions.
Did you know?
When automatic inventory tracking is enabled, Square automatically updates seller inventory counts whenever an Order
is completed through the Orders API. In this case, step one in the following example is done by Square when your Order
is completed.
- At 1:10 PM GMT, your application sends a BatchChangeInventory request (type
ADJUSTMENT
) to record a sale of 3 items. This reduces the in-stock count from 100 to 97. - At 1:20 PM, the POS goes offline. During this time, the seller:
- Sells 2 items.
- Starts counting their remaining inventory.
- At 1:30 PM, the seller counts 95 items. Your application sends a
BatchChangeInventory
request (typePHYSICAL_COUNT
) to update Square's system from its calculated count of 97 to the actual count of 95. - At 1:35 PM, the POS reconnects and reports the offline sale of 2 items (type
IN_STOCK
toSOLD
). Even though this update arrives last, Square applies it using its original timestamp (1:20 PM), before theBatchChangeInventory
physical count at 1:30 PM. The final count stays at 95 because the physical count has the latest timestamp. - At 1:40 PM, your application sends a
BatchChangeInventory
request to move 2 items fromIN_STOCK
toWASTE
. Because this timestamp is after the physical count, the system updates to 93 items in stock and 2 in waste.
InventoryPhysicalCount
should only be used to reconcile the inventory count computed by Square with the results of performing a physical count or syncing with a trusted external system.
Warning
Don't calculate inventory levels and return them to Square as a physical count. Your application might miss sales made through Square POS while offline, leading to incorrect calculations. A physical count adjustment should be based on an actual count of items at a location and posted with the correct occurred_at
time.
Consider the case where Square Point of Sale captures a sale after your application gets an inventory count from Square but before your application sends a new physical count.
In the following example, the application subtracted an arbitrary 3 units from the last calculated stock level and returned it as a new physical count. Had the seller done an actual physical count before sending the inventory physical count, they would have accounted for the 2 offline sales that were made. Because Square Point of Sale was offline, Square sent an inventory count of 10 rather than 8.
In this example, the application knows that 3 units were sold through a non-Square channel. It calls the Inventory API, which shows 10 units in IN_STOCK
. Meanwhile, Square Point of Sale sells 2 units, moving them from IN_STOCK
to SOLD
, leaving 8 units in IN_STOCK
.
The application incorrectly believes there are 10 units in IN_STOCK
. If it uses InventoryChange.type == PHYSICAL_COUNT
to reduce the count by three, it forces the count to seven units (10 – 3), instead of the correct five units (10 – 2 – 3).
If your application doesn't integrate the Orders API to track sales, you can track sales made through your application using BatchChangeInventory
with InventoryChange.type == ADJUSTMENT
, which ensures that inventory changes are applied in the correct order.
Square records inventory operations based on client timestamps and batched updates succeed or fail as atomic operations. For example, one request batches these changes for a single item variation:
- 100 units move from the
NONE
state to theIN_STOCK
state. - 5 units move from the
IN_STOCK
state to theWASTE
state. - Record a physical count of 90 units.
Each change in a batch request is recorded with its own timestamp, but all changes are applied together as a single request. Assuming the inventory count for the catalog item starts at zero:
- 100 units move from
NONE
toIN_STOCK
with a timestamp of 11:00 PM GMT, making the calculated inventory count 100. - 5 units move from
IN_STOCK
toWASTE
with a timestamp of 11:10 PM, making the calculated inventory count 95. - Square records a physical count of 90 units with a timestamp of 11:30 PM, resetting the calculated inventory count to 90.
If all three changes succeed, the new calculated inventory count for IN_STOCK
units is 90. However, if any of the individual changes fail, the entire update fails and the calculated inventory count remains unchanged at zero.
Inventory quantities are also affected by the Square Orders API and POS applications. For example, when the Square Point of Sale records an order, it moves 3 units from IN_STOCK
to SOLD
. If the order timestamp is after the batch update, the new IN_STOCK
count is 87.
NONE
isn't an actual inventory state; it's just a placeholder used when a new CatalogItemVariation
is initially added to the system. While inventory can move from NONE
to other states, nothing can move back to NONE
.
Items can move both in and out of the IN_STOCK
state. This movement doesn't reduce the total quantity, it just tracks which state items are in.
For example, if an item has 100 units and 3 units are damaged, you have 97 units in IN_STOCK
and 3 in WASTE
, still totaling 100 units. The IN_STOCK
state is special because Square Point of Sale and Square Dashboard use it to show available units for sale.
The SOLD
state is a final state where units are no longer tracked. Moving quantities from SOLD to another state adds new inventory instead of changing the SOLD
quantity.
For example, if "Small Leather Collar" has 100 units in IN_STOCK
and 8 units are sold, 92 units remain tracked. If 2 units are returned, there are 94 units tracked: 92 in IN_STOCK
and 2 in RETURNED_BY_CUSTOMER
.
Inventory state transitions represent real-world changes to inventory quantities. As a result, some state changes are permitted (such as IN_STOCK
to SOLD
) while others aren't (such as WASTE
to RETURNED_BY_CUSTOMER
). Square supports the following inventory state transitions:
From state | To state | Related event |
---|---|---|
NONE | IN_STOCK | A quantity of items was received and is available for sale. |
IN_STOCK | SOLD | A quantity of items was sold. |
IN_STOCK | WASTE | A quantity of items was damaged or lost and cannot be sold. |
UNLINKED_RETURN | IN_STOCK | A quantity of items was returned by the customer and is available for sale. The return isn't affiliated with a specific transaction. |
UNLINKED_RETURN | WASTE | A quantity of items was returned by the customer and deemed to be unsellable. The return isn't affiliated with a specific transaction. |
The Inventory API might also show additional read-only states in the change history of an item variation. Transitions to or from these read-only states can only be initiated by Square products.
If inventory tracking is enabled in the Square Dashboard, completing an itemized transaction with Square products automatically moves the quantity sold from the IN_STOCK
state to the SOLD
state.
For example, consider a Payment API request that references an Order object containing three catalog line items: a small leather dog collar, a 5-foot retractable leash, and Chewy chicken trainer treats.
When the CompletePayment or PayOrder endpoint is called to process the transaction, Square automatically adjusts the IN_STOCK
quantities by moving one unit of the small leather dog collar from IN_STOCK
to SOLD
, one unit of the 5-foot retractable leash from IN_STOCK
to SOLD
, and two units of Chewy chicken trainer treats from IN_STOCK
to SOLD
.
The BatchChangeInventory
endpoint has an ignore_unchanged_counts
flag (enabled by default) that skips inventory updates when:
- The new count matches the previous count.
- No adjustments occurred between counts.
This prevents duplicate entries in the Square Dashboard when third-party systems send repeated data. To record consecutive physical count adjustments with the same quantity, disable the flag.