---
title: 'Feature preview: Physical inventory'
description: >-
  Get early access to the physical inventory APIs (bins, counts, and purchase
  orders) in the unstable GraphQL Admin API, and test your app against them.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/orders-fulfillment/inventory-management-apps/physical-inventory-feature-preview
  md: >-
    https://shopify.dev/docs/apps/build/orders-fulfillment/inventory-management-apps/physical-inventory-feature-preview.md
---

# Feature preview: Physical inventory

The physical inventory feature preview gives you early access to a new set of inventory APIs in the unstable GraphQL Admin API. Use them to build against the primitives that model how merchants organize a stockroom: bins, counts, and purchase orders.

**Feature preview:**

These APIs are under active development and are subject to change. Enable the **Physical inventory** feature preview on a development store to try them, and share feedback so the stable surface is the one you'd want to build on.

These APIs are additive. They introduce new inventory primitives alongside the existing inventory APIs, so your current integration keeps working while you build against them.

***

## What's included

* **Bins.** Named storage within a location, such as a shelf or a rack. Create and update bins, and read the quantities held in each one.
* **Counts.** Set the on-hand quantity of an inventory item in a bin with `inventoryCountCreate`.
* **Purchase orders.** Read purchase order data through the public API: the purchase order, its line items, and its supplier.

***

## How bins work with your existing location inventory

Today you set an item's on-hand quantity at a location with `inventorySetQuantities`. Bins add a finer grain **inside** a location: instead of one on-hand number per location, an item's on-hand can be split across the bins where it's physically stored.

The on-hand that hasn't been placed in any bin is the location's **unbinned quantity**. A location's total on-hand is the unbinned quantity plus the quantity in every bin:

onHand = unbinnedQuantity + (sum of all bin quantities)

This keeps the preview compatible with the existing location-level APIs:

* Your existing `inventorySetQuantities` calls still work. Quantity set at the location level lands in the unbinned quantity — the bins you haven't touched are unaffected.
* The location-level aggregates (`available`, `committed`, `onHand`) are unchanged. They're still reported per item at the location; bins only change how the on-hand is distributed underneath.
* To place quantity into a specific bin, use a count (see [Set a quantity in a bin](#set-a-quantity-in-a-bin)).

***

## Enable the feature preview

Get early access by [enabling](https://shopify.dev/docs/api/feature-previews#enable-a-feature-preview) the **Physical inventory** feature preview on a development store.

**Caution:**

Feature previews are still under development and are subject to change.

Only stores with the preview enabled can call these APIs. Calls from a store that hasn't enabled the preview return an access error.

***

## Build against the APIs

Query these APIs against the `unstable` version of the GraphQL Admin API.

### Create and update bins

Use `inventoryBinUpsert` to create bins under a location, or update an existing bin's barcode. Provide a `name` (unique within the location) and an optional `barcode`.

```graphql
mutation {
  inventoryBinUpsert(
    locationId: "gid://shopify/Location/10"
    inventoryBins: [
      { name: "Shelf-A1", barcode: "012345678912" }
      { name: "Shelf-A2" }
    ]
  ) {
    inventoryBins {
      id
      name
      barcode
    }
    userErrors {
      field
      message
    }
  }
}
```

```json
{
  "data": {
    "inventoryBinUpsert": {
      "inventoryBins": [
        { "id": "gid://shopify/InventoryBin/2", "name": "Shelf-A1", "barcode": "012345678912" },
        { "id": "gid://shopify/InventoryBin/3", "name": "Shelf-A2", "barcode": null }
      ],
      "userErrors": []
    }
  }
}
```

### Read bin and location quantities

Read from the `location` query. `inventoryItemQuantities` returns the per-item aggregates for the location, and `inventoryBins` returns each bin with the quantities it holds per item.

```graphql
query {
  location(id: "gid://shopify/Location/10") {
    id
    name
    # Per-item aggregates for the location.
    inventoryItemQuantities(first: 10) {
      nodes {
        inventoryItem {
          id
          sku
        }
        available
        committed
        onHand
        unbinnedQuantity
      }
    }
    # Each bin and the quantity it holds per item.
    inventoryBins(first: 10) {
      nodes {
        id
        name
        barcode
        inventoryBinItemQuantities(first: 10) {
          nodes {
            inventoryItem {
              id
              sku
            }
            quantity
          }
        }
      }
    }
  }
}
```

```json
{
  "data": {
    "location": {
      "id": "gid://shopify/Location/10",
      "name": "Warehouse A",
      "inventoryItemQuantities": {
        "nodes": [
          {
            "inventoryItem": { "id": "gid://shopify/InventoryItem/1", "sku": "sku-a" },
            "available": 21,
            "committed": 2,
            "onHand": 23,
            "unbinnedQuantity": 5
          }
        ]
      },
      "inventoryBins": {
        "nodes": [
          {
            "id": "gid://shopify/InventoryBin/2",
            "name": "Shelf-A1",
            "barcode": "012345678912",
            "inventoryBinItemQuantities": {
              "nodes": [
                { "inventoryItem": { "id": "gid://shopify/InventoryItem/1", "sku": "sku-a" }, "quantity": 10 }
              ]
            }
          },
          {
            "id": "gid://shopify/InventoryBin/3",
            "name": "Shelf-A2",
            "barcode": null,
            "inventoryBinItemQuantities": {
              "nodes": [
                { "inventoryItem": { "id": "gid://shopify/InventoryItem/1", "sku": "sku-a" }, "quantity": 8 }
              ]
            }
          }
        ]
      }
    }
  }
}
```

Here `onHand` (23) is `unbinnedQuantity` (5) plus the two bins (10 + 8). `available` and `committed` stay at the location level.

### Set a quantity in a bin

Use `inventoryCountCreate` to set the on-hand quantity of an item in a bin. Each line item targets an inventory item and a bin, with a `quantity`:

* `actualQuantity` is the on-hand quantity to set for that item in that bin.
* `expectedQuantity` is the quantity you last read for that item in that bin. It's used to detect a variance and to guard against overwriting a change made since you read.

```graphql
mutation {
  inventoryCountCreate(
    locationId: "gid://shopify/Location/10"
    lineItems: [
      {
        inventoryItemId: "gid://shopify/InventoryItem/1"
        inventoryBinId: "gid://shopify/InventoryBin/2"
        quantity: { actualQuantity: 10, expectedQuantity: 9 }
      }
    ]
  ) {
    userErrors {
      field
      message
    }
  }
}
```

```json
{
  "data": {
    "inventoryCountCreate": {
      "userErrors": []
    }
  }
}
```

After the count is created, read the bin's quantities back with the [location query](#read-bin-and-location-quantities) to confirm the new on-hand.

### Read purchase orders

Read a single purchase order with `inventoryPurchaseOrder`, or a list with `inventoryPurchaseOrders`.

```graphql
query {
  inventoryPurchaseOrder(id: "gid://shopify/InventoryPurchaseOrder/1") {
    id
    name
    status
    supplier {
      name
    }
    lineItems(first: 10) {
      nodes {
        title
        supplierSku
        totalQuantity
      }
    }
  }
}
```

```json
{
  "data": {
    "inventoryPurchaseOrder": {
      "id": "gid://shopify/InventoryPurchaseOrder/1",
      "name": "#PO1001",
      "status": "OPEN",
      "supplier": { "name": "Acme Supply Co." },
      "lineItems": {
        "nodes": [
          { "title": "Widget", "supplierSku": "ACME-WIDGET-01", "totalQuantity": 100 }
        ]
      }
    }
  }
}
```

Query many purchase orders with `inventoryPurchaseOrders`:

```graphql
query {
  inventoryPurchaseOrders(first: 10) {
    nodes {
      id
      name
      status
      supplier {
        name
      }
    }
  }
}
```

***
