---
title: "Every variant with its stock, health and cover"
method: GET
path: "/api/v1/inventory/table"
tags: ["inventory"]
---

# Every variant with its stock, health and cover

`GET /api/v1/inventory/table`

## Request

- Base URL: `https://api.app.tightly.io`
- URL: `https://api.app.tightly.io/api/v1/inventory/table`
- Auth: HTTP bearer

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`.

## Query parameters

- `include_stock_availability` boolean
- `offset` integer
- `limit` integer
- `type` 'variants' | 'products'
- `filter_args` string: 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.
- `sort_args` string: 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).
- `search` string
- `distinct` 'variant_id' | 'product_id' | 'location_id'
- `fields` string
- `view` 'combined' | 'supplier_catalog'
- `bom_id` string
- `export` boolean

## Headers

- `Tightly-Version` string

## Response `200`

The inventory table.

- union
  - GetInventoryTableVariantViewResponse
    - `data` union
      - GetInventoryTableVariantViewPayload
        - `filtered_max_size` integer, required
        - `filtered_max_unique_size` integer, required
        - `offset` integer, required
        - `rows` InventoryTableVariantRow[], required
          - `available_locations` object[], nullable
          - `available_quantity` integer, nullable
          - `available_to_sell` integer
          - `barcode` string, nullable
          - `batch_size` integer, nullable
          - `capital_quadrant` string, nullable
          - `cover` unknown
          - `cover_exceeds_horizon` boolean
          - `cover_locations` object[], nullable
          - `custom_fields` object, nullable
          - `has_more_reliable_supplier` boolean
          - `health` string, nullable
          - `in_basket_quantity` integer, nullable
          - `is_managed` boolean, required
          - `last_stockout_date` string, nullable
          - `lead_time` integer, nullable
          - `lead_time_source` string, nullable
          - `location_id` string, nullable
          - `location_lead_times` object[]
          - `location_name` string, nullable
          - `metafields` object, nullable
          - `min_order_quantity` integer, nullable
          - `missing_fields` string[]
          - `next_arrival` string, nullable
          - `on_order` integer, nullable
          - `po_generation_enabled` boolean, required
          - `prepack` object, nullable
          - `prepack_id` string, nullable
          - `product_id` string, required
          - `product_image` string, nullable
          - `product_status` string, nullable
          - `product_title` string, nullable
          - `product_type` string, nullable
          - `production_type` string, required
          - `published_at` integer, nullable
          - `purchase_unit_definition` object, nullable
          - `replenishment_frequency` integer, nullable
          - `replenishment_set` union
            - ReplenishmentSetDetails
              - …
            - object, nullable
          - `reserved_quantity` integer, required
          - `sales_velocity` number, nullable
          - `sales_velocity_30_days` number, required
          - `sales_velocity_7_days` number, required
          - `sales_velocity_90_days` number, required
          - `sales_velocity_incl_bundles` number, nullable
          - `sell_price` number, nullable
          - `shopify_tags` string[], nullable
          - `sku` string, nullable
          - `stock_availability` union
            - StockAvailability
              - …
            - object, nullable
          - `stock_value` unknown
          - `supplier_details` SupplierDetails[], required
            - `batch_size` integer, nullable
            - `in_basket_quantity` integer, nullable
            - `is_default` boolean, nullable
            - `lead_time` integer, nullable
            - `lead_time_source` string, nullable
            - `min_order_quantity` integer, nullable
            - `otif_score` number, nullable
            - `replenishment_frequency` integer, nullable
            - `supplier_id` string, required
            - `supplier_name` string, nullable
            - `unit_cost` number, nullable
          - `supplier_id` string, nullable
          - `tags` string[], nullable
          - `unit_cost` number, nullable
          - `variant_id` string, required
          - `variant_title` string, nullable
          - `weeks_of_cover` unknown
        - `size` integer, required
      - object, nullable
    - `message` InventoryApiMessage
      - `desc` string
      - `service` string
      - `severity` 'SUCCESS' | 'INFO' | 'ERROR' | 'WARNING'
  - GetInventoryTableProductViewResponse
    - `data` union
      - GetInventoryTableProductViewPayload
        - `filtered_max_size` integer, required
        - `filtered_max_unique_size` integer, required
        - `offset` integer, required
        - `rows` InventoryTableProductRow[], required
          - `available_locations` object[], nullable
          - `batch_size` RangeField, required
            - `max` number, nullable
            - `min` number, nullable
          - `cover` RangeField, required
            - `max` number, nullable
            - `min` number, nullable
          - `cover_exceeds_horizon` HorizonFloorRange
            - `max` boolean
            - `min` boolean
          - `health` string, nullable
          - `last_stockout_date` string, nullable
          - `lead_time` RangeField, required
            - `max` number, nullable
            - `min` number, nullable
          - `location_id` string, nullable
          - `location_lead_times` RangeField
            - `max` number, nullable
            - `min` number, nullable
          - `location_name` string, nullable
          - `metafields` object, nullable
          - `min_order_quantity` RangeField, required
            - `max` number, nullable
            - `min` number, nullable
          - `missing_fields` string[]
          - `next_arrival` string, nullable
          - `performance_category` string, nullable
          - `product_id` string, required
          - `product_image` string, nullable
          - `product_status` string, nullable
          - `product_title` string, nullable
          - `product_type` string, nullable
          - `published_at` integer, nullable
          - `sales_velocity_30_days` number, required
          - `sales_velocity_7_days` number, required
          - `sales_velocity_90_days` number, required
          - `sell_price` RangeField, required
            - `max` number, nullable
            - `min` number, nullable
          - `shopify_tags` string[], nullable
          - `stock_availability` union
            - StockAvailability
              - …
            - object, nullable
          - `suppliers` string[], required
          - `total_available_quantity` integer, nullable
          - `total_in_basket_quantity` integer, required
          - `total_on_order_quantity` integer, required
          - `total_reserved_quantity` integer, required
          - `total_sales_velocity` number, required
          - `total_stock_value` unknown
          - `unit_cost` RangeField, required
            - `max` number, nullable
            - `min` number, nullable
          - `variants` object[], required
          - `weeks_of_cover` RangeField, required
            - `max` number, nullable
            - `min` number, nullable
        - `size` integer, required
      - object, nullable
    - `message` InventoryApiMessage
      - `desc` string
      - `service` string
      - `severity` 'SUCCESS' | 'INFO' | 'ERROR' | 'WARNING'
  - GetInventoryTableExportResponse
    - `data` union
      - GetInventoryTableExportPayload
        - `url` string, required
      - object, nullable
    - `message` InventoryApiMessage
      - `desc` string
      - `service` string
      - `severity` 'SUCCESS' | 'INFO' | 'ERROR' | 'WARNING'

## Other responses

- `401`: No usable key: malformed, unknown, revoked, expired or stopped. One refusal covers all five, telling a caller which is which maps the surface for them. A request with no Authorization header at all is refused 400 before any key is looked for.
- `403`: The key may not reach this operation. `scope_missing` when the key does not hold Stock; `organization_mismatch` when the path names an organisation that is not the key's; `ip_not_allowed` when the caller's address is outside the key's allowlist.
- `429`: The key has spent its allowance for this minute. 600 reads and 120 writes a minute, counted per key, refused calls included. `Retry-After` says how long to wait.
- `503`: The database is briefly out of reach, which is what a failover or a full connection pool looks like from outside. Wait the seconds `Retry-After` names and send the request again. A read is safe to repeat. A write is not, because the connection was lost in the middle of it, so read the object back before sending it a second time. Branch on the status rather than on `code`, which is `POSTGRES_UNAVAILABLE` from one of the two seams that raise this and `SERVICE_UNAVAILABLE` from the other.

---

[API](https://skmtc.dev/tightly/apis/tightly-api.md) · [All operations](https://skmtc.dev/tightly/apis/tightly-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tightly/apis/tightly-api/revisions/199f7eb35afb?raw)
