Every variant with its stock, health and cover
A page of stock: on hand, incoming, committed and available quantities, days of cover and a health word, with filtered_max_size for the filtered set's size.
type=variants (the default) answers one row per variant and location, carrying stock_value, available_to_sell and next_arrival; type=products aggregates to the product. distinct=variant_id or distinct=product_id collapses the location rows, view=combined answers one row per variant or product across locations, and export=true answers a download URL with fields naming its columns. Narrow with filter_args on location_id, supplier_id, health, managed, vendor and collection_id. Health is critical, caution or healthy.
cover_exceeds_horizon says whether days of cover is a measurement or a floor: true means the stock outlasted every day of forecast there was, so cover is how far the walk looked and reads as "364+ days" rather than a day it runs out on. It is a boolean on variant rows and a {min, max} pair of booleans on product rows and on view=combined, and it is false rather than null where the row carries no cover figure at all.
location_lead_times is an array of {location_id, location_name, lead_time, source} on variant rows, where source names the rung that won (variant_supplier_location, supplier_location, variant_supplier, supplier or organization), and a {min, max} range with no source on product rows. It is [] or {min: null, max: null} where nothing qualifies.
Scope: inventory:read.
Request
- Base URL: https://api.app.tightly.io
- URL: https://api.app.tightly.io/api/v1/inventory/table
- Auth: HTTP bearer
Query parameters
Return stock_availability per row: isp and wisp today; isp_30d and wisp_30d over the last 30 completed days of the organisation's calendar. A variant is out of stock when no eligible location holds positive available stock and it recorded no sale there that day. Levels carry forward until they change, up to 90 days, and never past the stock archive's last day. WISP weights by the next seven days of expected model units; a variant with no forecast weighs zero. Past WISP uses frozen one-week-ahead model units summed over the row's channels, never the current forecast. Historical WISP is unavailable for location-specific rows because saved weights are channel-grained. Unknown stock or weights are left out, and a percentage is null unless min_coverage (0.95) of its variants or variant-days, and of its weight, was measured. Fields include as_of, period_start, period_end, min_coverage, stock_basis, weight_basis, historical_weight_basis and observation counts. Uses the current filtered active managed variants and channel-mapped stock locations. Shared stock is not a channel allocation or proof that all demand can be filled. Percentages must not be summed or averaged across rows. Export uses availability_* columns.
The number of rows to skip before starting to return items.
The maximum number of rows to return.
View type - 'variants' shows individual variants, 'products' shows grouped by product.
JSON array of filter conditions. The endpoint rejects any key not in this allowlist.
Allowed keys (key · supported operations · notes):
- variant_id · eq, in, ne, nin
- product_id · eq, in
- shopify_status · eq, in
- supplier · eq, in, ne · supplier name (string)
- supplier_id · eq, in, ne · any linked supplier
- default_supplier_id · eq, in, ne · variant's default supplier
- product_type · eq · values: 'continuity' | 'new'
- category · eq, in
- in_stock · gte, lte · integer
- stock_value · gte, lte · numeric
- available_to_sell · gte, lte · integer
- next_arrival · gte, lte, gt, lt, eq · ISO date string
- managed_status · eq · values: 'managed' | 'unmanaged'
- tags · eq, in · values: 'high_growth' | 'declining' | 'not_selling' | 'consistent_seller' | 'stocked_out' | 'overstocked' | 'understocked' | 'new_arrival' | 'discontinued' | 'supplier_issues'. Use this key (not 'health') to filter by overstocked / understocked / stocked_out status.
- out_of_stock · eq · boolean
- weeks_of_cover · gte, lte · numeric
- shopify_tags · eq, in
- class · eq, in · performance category
- unit_cost · gte, lte · numeric
- sell_price · gte, lte · numeric
- date_published · gt, lt, gte, lte, eq · ISO date string
- lead_time · gte, lte · integer (days). On the per-location rows (type=variants with no distinct, and, since this change, type=products with no distinct) this filters the same 5-level per-warehouse resolution the row displays (variant_supplier_locations -> supplier_locations -> variant_suppliers -> suppliers -> org default). Every other shape (variants&distinct=variant_id, variants&view=combined, products&distinct=product_id, products&view=combined) filters the flat variant_suppliers -> suppliers -> org default chain, matching that shape's own flat lead_time field.
- location · eq, in, ne · location name
- location_id · eq, in, ne
- health · eq, in · values: 'critical' | 'caution' | 'healthy' (case-insensitive)
- recommended_quantity · gte, lte · integer
- replenishment_set_id · eq, in, ne
- collection_id · eq, in, ne, nin
- missing_fields · eq, in · values: 'product_title' | 'price' | 'lead_time' | 'unit_cost' | 'min_order_quantity' | 'supplier' | 'location' | 'shopify_tags'
- purchase_order_id · ne only
- event_start_date · gte, eq · ISO date string
- event_end_date · lte, eq · ISO date string
- event_sales_channel_ids · eq, in
- successor_variant_id · eq · value must be null
- predecessor_variant_id · eq · value must be null
- production_type · eq, in · values: 'buy_only' | 'raw_material' | 'manufacturable'
- prepack_id · eq, ne
Dynamic prefixes: keys starting with 'metafields.' or 'custom_fields.' are also accepted.
Not filterable here (response/sort only, do not put in filter_args): min_order_quantity, batch_size, sku, barcode, variant_title, product_title, sales_velocity, cover, on_order, in_basket_quantity.
Example: '[{"key":"stock_value","operation":"gte","value":100},{"key":"available_to_sell","operation":"lte","value":5},{"key":"next_arrival","operation":"gte","value":"2025-01-01"}]' To filter by supplier, pass filter_args: [{"key": "supplier_id", "operation": "eq", "value": "<id>"}]. Passing supplier_id as a direct query parameter is not supported and will be silently ignored.
A JSON array of {key, operation, value}. The allowlist of keys and operations, and what each one means, is on the schema below.
Comma-separated sort columns. Prefix with + for asc, - for desc. Supported columns include stock_value, available_to_sell, next_arrival, total_stock_value, variant_title, sku, barcode, unit_cost, lead_time, location_lead_times, replenishment_frequency, supplier_name, location_name, sales_velocity, sales_velocity_7_days, sales_velocity_30_days, sales_velocity_90_days, cover, weeks_of_cover, in_basket_quantity, last_stockout_date, reserved_quantity, production_type, is_managed, replenishment_set, and others. Example: '-stock_value,+replenishment_set' or '+variant_title'
'location_lead_times' sorts by the fastest warehouse the row reports: the smallest 'lead_time' among the array entries on variant-grain rows, the 'min' of the range on product-grain rows. Both directions order by that same minimum ('-location_lead_times' does NOT switch to the range's 'max'), and rows reporting nothing ([] / {"min": null}) sort last either way. It is NOT interchangeable with 'lead_time': on the variant-grain (distinct=variant_id, view=combined) and distinct=product_id shapes the 'lead_time' column is the flat variant_suppliers -> suppliers -> org default chain, so the two sorts legitimately differ. Under view=supplier_catalog it follows the filtered supplier, like the range itself. It is a sort key only. 'location_lead_times' is not filterable (filter on 'lead_time' instead).
Comma-separated columns to sort by; prefix a column with - for descending.
Search term for filtering results.
Comma-separated distinct columns (e.g. variant_id,location_id).
Comma-separated list of fields to return in each row.
When set to 'combined', returns a product-level view without requiring type=products. When set to 'supplier_catalog', 'location_lead_times' resolves for the supplier of the request's supplier_id eq filter instead of each variant's default supplier; the request MUST carry a supplier_id eq filter in filter_args (e.g. [{"key":"supplier_id","operation":"eq","value":"<supplier_id>"}]) or it is rejected with 400. All other columns keep their default-view values.
Bill of materials ID to filter by.
When true, returns an export URL instead of table data.
Headers
The date train to answer on. One train exists today, 2026-11, the one this reference describes and the one every key is minted on, so the call answers on it with or without this header. Send it anyway, so a client built now names its train from the start.
Response
The inventory table.
Changes
No recorded changes to this operation across all 1 revision of this API.