Back to Developer

Inventory Management

Multi-location stock tracking, transfers, audits, cost layers, and unit-level QR tracking.

Overview

The inventory API gives you real-time visibility into stock across every location. Track quantities at the product and variant level, move stock between warehouses and stores, run cycle count audits, maintain cost layers for accurate COGS reporting, and optionally track individual units by serial number or QR code.

Multi-location

Track stock independently at each warehouse, store, or fulfillment center.

Real-time levels

Quantities update instantly on sales, returns, adjustments, and transfers.

Transfers

Move stock between locations with full audit trail and status tracking.

Cycle counts

Run inventory audits with variance detection and reconciliation.

Cost layers

FIFO, LIFO, or weighted average costing for accurate COGS.

Unit tracking

Serial numbers and QR codes for individual item lifecycle tracking.

Stock Levels

Query current stock quantities by product, variant, or location. Each inventory record includes the on-hand quantity, committed (reserved for orders), and available (on-hand minus committed).

const whale = new WhaleClient("wk_live_...");

// List stock at a specific location
const stock = await whale.inventory.list({
  location_id: "loc_warehouse_east",
  limit: 50
});

// Response
{
  "object": "list",
  "data": [
    {
      "id": "inv_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "product_id": "prod_001",
      "variant_id": "var_001_blue_m",
      "location_id": "loc_warehouse_east",
      "on_hand": 150,
      "committed": 12,
      "available": 138,
      "reorder_point": 25,
      "updated_at": "2026-03-10T08:30:00.000Z"
    }
  ]
}

Adjust Stock

Increase or decrease stock with a reason code for audit compliance. Adjustments create a permanent ledger entry — quantities are never silently changed. Common reasons include received, damaged, shrinkage, and correction.

// Add stock after receiving a shipment
await whale.inventory.adjust({
  product_id: "prod_001",
  variant_id: "var_001_blue_m",
  location_id: "loc_warehouse_east",
  quantity_change: 50,
  reason: "received",
  reference: "PO-2026-0342",
  notes: "Spring collection restock"
});

// Remove damaged units
await whale.inventory.adjust({
  product_id: "prod_001",
  variant_id: "var_001_blue_m",
  location_id: "loc_warehouse_east",
  quantity_change: -3,
  reason: "damaged",
  notes: "Water damage in transit"
});

// Response
{
  "object": "inventory_adjustment",
  "id": "adj_f1e2d3c4-b5a6-7890-abcd-ef1234567890",
  "product_id": "prod_001",
  "location_id": "loc_warehouse_east",
  "quantity_change": -3,
  "reason": "damaged",
  "previous_on_hand": 200,
  "new_on_hand": 197,
  "created_at": "2026-03-10T09:15:00.000Z"
}

Transfer Between Locations

Move stock from one location to another. Transfers go through a lifecycle: pending (created), in_transit (shipped between locations), and completed (received at destination). Source quantities are decremented immediately; destination quantities increase on completion.

const transfer = await whale.inventory.transfer({
  from_location: "loc_warehouse_east",
  to_location: "loc_store_downtown",
  items: [
    {
      product_id: "prod_001",
      variant_id: "var_001_blue_m",
      quantity: 20
    },
    {
      product_id: "prod_002",
      variant_id: "var_002_black_l",
      quantity: 10
    }
  ],
  notes: "Restocking downtown store for weekend sale"
});

// Response
{
  "object": "inventory_transfer",
  "id": "txfr_c3d4e5f6-a7b8-9012-cdef-1234567890ab",
  "status": "pending",
  "from_location": "loc_warehouse_east",
  "to_location": "loc_store_downtown",
  "items": [
    { "product_id": "prod_001", "variant_id": "var_001_blue_m", "quantity": 20 },
    { "product_id": "prod_002", "variant_id": "var_002_black_l", "quantity": 10 }
  ],
  "created_at": "2026-03-10T10:00:00.000Z"
}

Inventory Audits

Run cycle count audits to verify physical stock matches system records. Create an audit for a location, submit counted quantities, and the system calculates variances automatically. Audits can be approved to reconcile discrepancies.

// Create a cycle count audit
const audit = await whale.inventoryAudits.create({
  location_id: "loc_store_downtown",
  type: "cycle_count",
  notes: "Monthly cycle count — Zone A shelves"
});

// Submit counted items
await whale.inventoryAudits.update(audit.id, {
  status: "counted",
  items: [
    {
      product_id: "prod_001",
      variant_id: "var_001_blue_m",
      expected_quantity: 45,
      counted_quantity: 43
    },
    {
      product_id: "prod_002",
      variant_id: "var_002_black_l",
      expected_quantity: 30,
      counted_quantity: 30
    }
  ]
});

// Response
{
  "object": "inventory_audit",
  "id": "aud_d4e5f6a7-b8c9-0123-defg-234567890abc",
  "location_id": "loc_store_downtown",
  "status": "counted",
  "type": "cycle_count",
  "total_items": 2,
  "variance_count": 1,
  "items": [
    {
      "product_id": "prod_001",
      "expected_quantity": 45,
      "counted_quantity": 43,
      "variance": -2
    },
    {
      "product_id": "prod_002",
      "expected_quantity": 30,
      "counted_quantity": 30,
      "variance": 0
    }
  ],
  "created_at": "2026-03-10T11:00:00.000Z"
}

Cost Layers

Track the landed cost of inventory using FIFO, LIFO, or weighted average methods. Each receipt creates a cost layer with the purchase price, shipping, duties, and other landed costs. When stock is sold, cost layers are consumed according to the configured method for accurate COGS reporting.

// List cost layers for a product
const layers = await whale.inventoryCostLayers.list({
  product_id: "prod_001",
  limit: 10
});

// Response
{
  "object": "list",
  "data": [
    {
      "id": "cl_e5f6a7b8-c9d0-1234-efgh-345678901bcd",
      "product_id": "prod_001",
      "variant_id": "var_001_blue_m",
      "costing_method": "fifo",
      "unit_cost": 12.50,
      "landed_cost": 14.20,
      "quantity_received": 100,
      "quantity_remaining": 67,
      "reference": "PO-2026-0298",
      "received_at": "2026-02-15T08:00:00.000Z"
    },
    {
      "id": "cl_f6a7b8c9-d0e1-2345-fghi-456789012cde",
      "product_id": "prod_001",
      "variant_id": "var_001_blue_m",
      "costing_method": "fifo",
      "unit_cost": 13.00,
      "landed_cost": 14.85,
      "quantity_received": 50,
      "quantity_remaining": 50,
      "reference": "PO-2026-0342",
      "received_at": "2026-03-10T09:00:00.000Z"
    }
  ]
}

Unit Tracking

Track individual items by serial number or QR code throughout their lifecycle. Each unit records its current status, location, and full history — from receipt to sale or return. Useful for high-value goods, warranty tracking, and anti-counterfeiting.

// Register a new tracked unit
const unit = await whale.inventoryUnits.create({
  product_id: "prod_003",
  variant_id: "var_003_gold",
  serial_number: "SN-2026-00451",
  qr_code: "https://wh.al/u/SN-2026-00451",
  location_id: "loc_warehouse_east",
  cost: 245.00,
  metadata: {
    batch: "B2026-03",
    inspection_date: "2026-03-08"
  }
});

// List units with filters
const units = await whale.inventoryUnits.list({
  product_id: "prod_003",
  status: "in_stock",
  location_id: "loc_warehouse_east"
});

// Response
{
  "object": "list",
  "data": [
    {
      "id": "unit_a7b8c9d0-e1f2-3456-ghij-567890123def",
      "product_id": "prod_003",
      "serial_number": "SN-2026-00451",
      "qr_code": "https://wh.al/u/SN-2026-00451",
      "status": "in_stock",
      "location_id": "loc_warehouse_east",
      "cost": 245.00,
      "history": [
        { "event": "received", "at": "2026-03-09T08:00:00.000Z", "location": "loc_warehouse_east" },
        { "event": "inspected", "at": "2026-03-09T10:30:00.000Z", "by": "staff_mike" }
      ],
      "created_at": "2026-03-09T08:00:00.000Z"
    }
  ]
}

API Reference

MethodPathDescription
GET/v1/stores/{store_id}/inventoryList stock levels. Filter by location_id, product_id, or variant_id.
GET/v1/stores/{store_id}/inventory/{id}Get a single inventory record with quantity breakdown.
POST/v1/stores/{store_id}/inventory/adjustAdjust stock quantity with a reason code.
POST/v1/stores/{store_id}/inventory/transferTransfer stock between two locations.
GET/v1/stores/{store_id}/inventory-auditsList inventory audits. Filter by location_id or status.
GET/v1/stores/{store_id}/inventory-audits/{id}Get a single audit with counted items.
POST/v1/stores/{store_id}/inventory-auditsCreate a new cycle count audit for a location.
PATCH/v1/stores/{store_id}/inventory-audits/{id}Update audit status or submit counted items.
GET/v1/stores/{store_id}/inventory-cost-layersList cost layers. Filter by product_id or costing method.
GET/v1/stores/{store_id}/inventory-cost-layers/{id}Get a single cost layer with receipt history.
GET/v1/stores/{store_id}/inventory-unitsList tracked units. Filter by serial_number, product_id, or status.
GET/v1/stores/{store_id}/inventory-units/{id}Get a single unit with full lifecycle history.
POST/v1/stores/{store_id}/inventory-unitsRegister a new tracked unit with serial number or QR code.
PATCH/v1/stores/{store_id}/inventory-units/{id}Update unit status, location, or metadata.