Stock and availability
The storefront API never answers a stock quantity. It tells you whether a product or variant can be bought and whether stock is running low, and the cart refuses a quantity it cannot fill.
What the API tells you
| Field | On | Meaning |
|---|---|---|
availability | Products and variants | in_stock, out_of_stock, preorder, backorder or null (not set). |
lowStock | Products and variants | A tracked variant at or below the store’s low-stock threshold. A product whose stock is all held in checkouts answers false (it is out of stock instead). |
purchasable | Full products and variants | Public, priced in the answer’s currency, and available or backorderable. |
That is all: no counts, ever. A storefront can show “In stock”, “Only a few left” or “Sold out”, never “3
left”. The one number the API answers is maxQuantity, and only when a requested
quantity is refused (below). Stock is counted only for variants that track inventory, and a tracked variant
that allows backorders is never refused for stock.
import { createCatalogClient } from '@usethrottle/catalog';
const catalog = createCatalogClient({
publishableKey: 'pc_pk_your_browser_key',
});
const answer = await catalog.products.get('ceramic-brake-pads');
if (!('redirectTo' in answer)) {
for (const variant of answer.product.variants) {
// No quantities: availability, lowStock and purchasable are all there is.
const label = !variant.purchasable
? 'Unavailable'
: variant.availability === 'backorder'
? 'Ships when back in stock'
: variant.lowStock
? 'Only a few left'
: 'In stock';
console.log(variant.sku, label);
}
}
// Check lines without creating a cart, for example when a cart page loads.
const check = await catalog.carts.check([{ sku: 'BRK-PAD-01', quantity: 12 }]);
for (const item of check.items) {
if (item.ok || item.code !== 'QUANTITY_UNAVAILABLE') continue;
// maxQuantity can be 0: other shoppers' checkouts hold the last units.
console.log(
item.maxQuantity === 0
? 'In other shoppers’ checkouts right now. Try again in a few minutes.'
: `Only ${item.maxQuantity} can be ordered.`,
);
}
Reserve stock at checkout
Reserve stock at checkout is a per-store setting in the Product Catalog dashboard, under Storefront › Settings › Stock. It is off by default; with it off, stock is the quantity you import or enter, and nothing below happens. With it on, the catalog keeps its own count:
- Holds at checkout. When a shopper starts checkout from a catalog cart, the catalog holds the cart's tracked lines for 15 minutes, only if every line fits; otherwise it holds nothing and refuses the lines that do not fit. Starting checkout again replaces the cart's holds. A cart's own holds never count against it.
- Holds lapse on their own. An unpaid hold simply expires; nothing has to release it. An abandoned cart releases its holds earlier when Throttle reports it.
- Sales. An order takes its catalog lines off stock and releases its cart's holds.
- Cancels and returns. A cancelled order puts its lines back, and so does a return once it is received with restocking on. A refund of money alone changes no stock.
- Counting starts from the quantities you have when you turn it on; earlier orders never move stock. A new quantity from an import or the API becomes the new starting point.
Turning it on needs the Product Catalog extension version that reads carts and returns; the dashboard says so if the store has not accepted it yet.
Oversold orders
The order is the truth. A paid order is never refused or changed because of stock: its sale is always recorded, even when that takes a variant below zero. This can happen because Throttle's checkout session lasts 30 minutes, longer than the 15-minute hold, so a shopper who pays late can buy units another checkout has since taken.
See Cart and checkout for every cart error, and Line resolvers for how Throttle asks the catalog to check stock.