How It Works
A deeper look at the Inventory API
Inventory unit counts are calculated by applying all the adjustments received since the last recorded physical count. If no physical count has been recorded, the adjustments are applied with an assumed starting unit quantity of 0.
Inventory changes (including changes due to sales transactions) can be sent to
Square out of order. As a result, adjustments and physical count updates require
a client-specified RFC 3339 timestamp
occurred_at) so the inventory history can be ordered correctly.
Consider a situation where the Square Point of Sale app processes transactions in offline mode while a custom inventory management solution makes inventory adjustments through the API.
Assume the related item variation starts with 100 IN_STOCK units.
At 13:10 GMT, the inventory management backend sends an
InventoryAdjustmentrequest using the Inventory API to record a sale through a non-Square system and move 3 units from
SOLD. At this point, there are 97
At 13:20 GMT, the Square Point of Sale device goes offline. The merchant sells 2 units in offline mode with a recorded transaction time of 13:20 GMT.
At 13:30 GMT, the inventory management backend sends an
InventoryPhysicalCountrequest using the Inventory API to reconcile the computed quantity in the Square inventory service (97 units) with a verified physical quantity available for sale (90 units).
At 13:35 GMT, the Square Point of Sale device reconnects to the internet and pushes the offline sale to Square, which moves 2
SOLD. Square applies the transaction results, with a client timestamp of 13:20 GMT, before the
InventoryPhysicalCountrequest that occurred at 13:30 GMT. Although the Point of Sale transaction was the last change sent to Square, the
IN_STOCKquantity is still 90 units, as indicated by the physical count update, because the last change according to client timestamp was the
At 13:40 GMT, the inventory management backend sends another
InventoryAdjustmentrequest using the Inventory API to move 2 from
WASTEbecause they are no longer suitable for sale. The
InventoryAdjustmentrequest has a client timestamp that happens after the count reconciliation so the
IN_STOCKcount is now 88 units and the
WASTEcount is 2 units.
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. DO NOT use
InventoryPhysicalCount to apply sequential adjustments to
CatalogItemVariation quantities. Retrieving an
InventoryCount from the server, modifying the count based on recent changes, then pushing the updated inventory count as an
InventoryPhysicalCount forces Square to ignore changes that may have occurred in the interim and results in inaccurate tracking.
Consider the case where the Square Point of Sale app captures a sale after the
InventoryCount request but before the
Assume an application knows that 3 units were sold through a non-Square channel. The application calls the Inventory API and the
InventoryCount result indicates 10 units in the
IN_STOCK state. At the same time, the Square Point of Sale application captures a sale of 2 units and moves those 2 units from
SOLD. There are now 8 units in the
Based on the
InventoryCount result, the application incorrectly believes there are currently 10 units in the
IN_STOCK state. If the application uses
InventoryPhysicalCount to reduce the
IN_STOCK quantity by 3, the final quantity of
IN_STOCK units is forced to be 7 units (10
IN_STOCK units − 3 units sold externally) when it should be 5 units (10
IN_STOCK units − 2 units sold through Square Point of Sale − 3 units sold externally).
The correct way to track sales through a non-Square system is to push an
InventoryAdjustment that moves units from
SOLD. Inventory adjustments force Square systems to apply inventory changes in the correct order rather than explicitly overwriting the count. For example, by subtracting the 3 units sold externally after subtracting the 2 units sold through Square Point of Sale app.
Square servers record inventory operations based on timestamps provided by the communicating client and batched updates succeed or fail as atomic operations. For example, consider the case where a single request batches the following changes for a single item variation:
100 units move from the
NONEstate to the
5 units move from the
IN_STOCKstate to the
record a physical count of 90 units.
The individual changes in a batched state changes are recorded based on their individual client timestamps but they are applied, all-or-nothing, as a single request. Assuming the inventory count for the targeted catalog item variation starts at 0:
100 units move from
IN_STOCKwith a timestamp of 23:00 GMT, making the calculated inventory count 100.
5 units move from
WASTEwith a timestamp of 23:10 GMT, making the calculated inventory count 95.
The system records a physical count of 90 units with a timestamp of 23:30 GMT, resetting the calculated inventory count to 90 rather than 95.
If all three changes succeed, the new calculated inventory count for
IN_STOCK units is 90. But if any of the individual changes fail, the entire update fails and the calculated inventory count will remain unchanged at 0.
Inventory quantities are also affected by Square payment APIs and Point of Sale applications. In the example above, when the Square Point of Sale app records a transaction, it moves 3 units from the
IN_STOCK state to the
SOLD state. Assuming the transaction timestamp places it after the batch update succeeds, the new calculated inventory count for
IN_STOCK units will be 87 units.
NONE state is not a true inventory state.
NONE is a
from_state placeholder to represent the fact that a given
CatalogItemVariation was introduced as new inventory. Inventory quantities can be transitioned from the
NONE state, but cannot be transitioned to the
IN_STOCK state does not represent a pool of available items decremented over time. Item variation quantities move into, and out of, the
IN_STOCK state as they do with any other state. In general, when quantities move between states, the total quantity for that item variation across all states does not change. For example, consider the item variation "Small Leather Collar" with a total of 100 units. Initially all 100 units are in the
IN_STOCK state. If 3 units become damaged, there are still 100 units at the end of the day: 97 units in the
IN_STOCK state and 3 in the WASTE state. The
IN_STOCK state is only special in that the Square Point of Sale app and Square Dashboard use the quantity of item variations with
IN_STOCK state to determine the number of units currently available for sale.
SOLD is a terminal state. When inventory items move to the
SOLD state, the units are no longer explicitly tracked. Transferring quantities from
SOLD to some other state introduces a new quantity into inventory rather than changing the quantity in the
SOLD state. For example, consider the case where "Small Leather Collar" has 100 units in the
IN_STOCK state. If 5 units are sold online and 3 units are sold in the store, there will be 92 units tracked at the end of the day. If a customer then returns 2 units in the store, there will be 94 units tracked: 92 units with
IN_STOCK state and 2 units with
Inventory state transitions represent real world changes to inventory quantities. As a result, some state changes are permitted (e.g.,
SOLD) while others are not (e.g.,
RETURNED_BY_CUSTOMER). Square supports the following inventory state transitions:
|From State||To State||Related Event|
|A quantity of items was received and is available for sale.|
|A quantity of items was sold.|
|A quantity of items was damaged or lost and cannot be sold.|
|A quantity of items was returned by the customer and is available for sale and the return is not affiliated with a specific transaction.|
|A quantity of items was returned by the customer and deemed to be unsellable and the return is not affiliated with a specific transaction.|
In addition to the support state transitions above, the Inventory API may return additional, read-only states as part of the change history for a given item variation. Transitions to or from read-only states can only be triggered from within Square products.
If inventory tracking is enabled in Square Dashboard, completing an itemized transaction with Square products automatically moves the quantity sold from the
IN_STOCK state to the
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 chicken "chewy trainer" treats.
When the Charge endpoint processes the transaction, Square automatically adjusts the
IN_STOCK quantities by moving 1 unit of the small leather collar from
SOLD, 1 unit of the 5 foot retractable leash from
SOLD, and 2 units of Chewy chicken trainer treats from
Requests to the BatchChangeInventory endpoint include an
ignore_unchanged_counts flag. The
ignore_unchanged_counts flag tells Square to skip updates for the
CatalogItemVariation if nothing has changed. When the new physical count for a
CatalogItemVariation is the same as the previous physical count and no
InventoryAdjustment requests have been received between the two physical counts, the physical count update is skipped. The
ignore_unchanged_counts flag lets third-party systems push potentially redundant inventory counts to Square without polluting the Square Dashboard with necessary adjustments.
ignore_unchanged_counts is enabled by default so developers do not inadvertently spam Square servers with redundant
InventoryPhysicalCount requests. Do not disable the
ignore_unchanged_counts flag unless the associated
InventoryPhysicalCount request represents a true reconciliation, such as an actual physical count by a person.