IOM
IOM
API Reference
Back to Home
REST API · JSON · Bearer Auth

IOM API Reference

The IOM REST API lets you read and write inventory, orders, customers, warehouses, and more. All endpoints return JSON and follow standard HTTP conventions.

Base URL https://iom.beezifi.com/api
Authentication
The API supports two authentication methods depending on your use case.
User JWT (Bearer token) — For requests on behalf of a logged-in user. Obtain a token via POST /api/auth/login, then include it in every request as Authorization: Bearer <token>. Tokens expire after 8 hours.
API Key (X-API-Key header) — For server-to-server integrations. Create keys in Settings → API Keys. Pass as X-API-Key: iom_live_xxxx. API keys support scoped permissions (read / write).
Example — Login and use token
# 1. Get a token
POST /api/auth/login
Content-Type: application/json

{
  "email": "admin@acme.com",
  "password": "your-password"
}

# Response → { "token": "eyJhbGci..." }

# 2. Use the token
GET /api/products
Authorization: Bearer eyJhbGci...
Example — API Key
GET /api/external/products
X-API-Key: iom_live_abc123xyz

Errors
All errors return a JSON object with an error field and an appropriate HTTP status code.
StatusMeaning
200Success
201Created
400Bad request — validation error or missing field
401Unauthorized — missing or expired token
402Subscription expired — account needs renewal
403Forbidden — insufficient role or banned account
404Not found
409Conflict — duplicate SKU, email, etc.
500Internal server error
{
  "error": "SKU already exists for this tenant"
}

Pagination
List endpoints support page and limit query parameters. Responses include a total count.
GET /api/products?page=2&limit=25

# Response
{
  "products": [ ... ],
  "total": 142,
  "page":  2,
  "limit": 25
}

Products
Create and manage products including SKUs, pricing, categories, images, and variant options.
GET /api/products List all products
Returns a paginated list of products. Supports full-text search, category filter, and active filter.
Query Parameters
NameTypeRequiredDescription
searchstringoptionalFull-text search on name and SKU
categoryintegeroptionalFilter by category ID
activebooleanoptionalFilter by active status (default: all)
pageintegeroptionalPage number (default: 1)
limitintegeroptionalResults per page (default: 50, max: 200)
Response
{
  "products": [
    {
      "id":             7,
      "name":           "Widget Pro 500",
      "sku":            "WGT-500",
      "base_price":    "49.99",
      "cost_price":    "18.00",
      "barcode":       "9312345678901",
      "vendor":        "WidgetCo",
      "category_id":   3,
      "category_name": "Hardware",
      "tracking_type": "batch",
      "is_active":     true,
      "total_stock":   320
    }
  ],
  "total": 48,
  "page":  1,
  "limit": 50
}
GET /api/products/:id Get a product
Returns a single product by ID including images, variants, and current stock levels.
Response
{
  "id":               7,
  "name":             "Widget Pro 500",
  "sku":              "WGT-500",
  "base_price":      "49.99",
  "cost_price":      "18.00",
  "compare_at_price": "59.99",
  "barcode":         "9312345678901",
  "vendor":          "WidgetCo",
  "description":     "Heavy-duty widget with 500N load rating.",
  "category_id":     3,
  "tracking_type":   "batch",
  "weight":          "0.450",
  "taxable":         true,
  "requires_shipping": true,
  "is_active":       true,
  "images": [
    { "id": 12, "url": "/uploads/products/wgt500.jpg", "is_primary": true }
  ],
  "stock": [
    { "warehouse_id": 1, "warehouse_name": "Sydney",    "quantity": 200 },
    { "warehouse_id": 2, "warehouse_name": "Melbourne", "quantity": 120 }
  ],
  "created_at": "2025-11-03T08:14:22Z"
}
POST /api/products Create a product
Admin / Manager only
Body Parameters
NameTypeRequiredDescription
namestringrequiredProduct name
skustringrequiredUnique SKU within tenant
base_pricenumberrequiredSelling price
cost_pricenumberoptionalCost / landed price
category_idintegeroptionalCategory ID
barcodestringoptionalBarcode / EAN / UPC
vendorstringoptionalBrand or vendor name
tracking_typeenumoptionalnone | batch | serial
weightnumberoptionalWeight in kg
descriptionstringoptionalProduct description
Request body
{
  "name":          "Widget Pro 500",
  "sku":           "WGT-500",
  "base_price":    49.99,
  "cost_price":    18.00,
  "tracking_type": "batch"
}
Response — 201 Created
{
  "id":            52,
  "name":          "Widget Pro 500",
  "sku":           "WGT-500",
  "base_price":    "49.99",
  "cost_price":    "18.00",
  "tracking_type": "batch",
  "is_active":     true,
  "created_at":    "2026-05-06T11:22:05Z"
}
PUT /api/products/:id Update a product
Admin / Manager only
Full replacement of product fields. Same body as POST.
Response
{ "message": "Product updated" }
DELETE /api/products/:id Delete a product
Admin only
Permanently deletes the product. Cannot be undone.
Response
{ "message": "Product deleted" }
GET /api/products/categories List categories
Returns all product categories for this tenant including hierarchy.
Response
{
  "categories": [
    { "id": 1, "name": "Fasteners", "parent_id": null, "product_count": 24 },
    { "id": 2, "name": "Bolts",     "parent_id": 1,    "product_count": 12 },
    { "id": 3, "name": "Hardware",  "parent_id": null, "product_count": 48 }
  ]
}
GET /api/products/export Export products CSV
Downloads all products as a CSV file. Response is text/csv with Content-Disposition: attachment.

Inventory
Query stock levels, adjust quantities, transfer stock between warehouses, and retrieve movement history.
GET /api/inventory List stock levels
Query Parameters
NameTypeRequiredDescription
warehouse_idintegeroptionalFilter by warehouse
low_stockbooleanoptionalOnly show items below reorder level
Response
{
  "inventory": [
    {
      "product_id":       7,
      "sku":              "WGT-500",
      "product_name":    "Widget Pro 500",
      "warehouse_id":    1,
      "warehouse_name":  "Sydney",
      "quantity":        200,
      "reorder_level":   50,
      "reserved_quantity": 12
    }
  ],
  "total": 156
}
GET /api/inventory/summary Inventory summary stats
Returns total SKU count, total stock value, low stock count, and out-of-stock count.
Response
{
  "total_skus":        48,
  "total_stock_value": 84320.50,
  "low_stock_count":   6,
  "out_of_stock_count": 2,
  "warehouses":        3
}
GET /api/inventory/alerts Low stock alerts
Returns all products where current quantity is at or below the configured reorder level.
Response
{
  "alerts": [
    {
      "product_id":     14,
      "sku":            "BLT-M820",
      "product_name":  "Bolt M8x20",
      "warehouse_id":  1,
      "warehouse_name": "Sydney",
      "quantity":       18,
      "reorder_level":  50
    }
  ],
  "total": 6
}
GET /api/inventory/movements Movement history
Returns all stock movements (receives, adjustments, transfers, shipments) with timestamps and user attribution.
Response
{
  "movements": [
    {
      "id":              309,
      "product_id":     7,
      "sku":            "WGT-500",
      "movement_type":  "adjustment",
      "quantity":       50,
      "warehouse_id":   1,
      "warehouse_name": "Sydney",
      "reference_id":   null,
      "notes":          "Cycle count correction",
      "created_by_name": "Jane Doe",
      "created_at":     "2026-05-05T14:30:00Z"
    }
  ],
  "total": 1240
}
POST /api/inventory/adjust Adjust stock quantity
Admin / Manager only
Body Parameters
NameTypeRequiredDescription
product_idintegerrequiredProduct to adjust
warehouse_idintegerrequiredWarehouse to adjust in
quantitynumberrequiredPositive (add) or negative (remove)
notesstringoptionalReason for adjustment
Response
{ "message": "Inventory adjusted", "new_quantity": 250 }
POST /api/inventory/transfer Transfer between warehouses
Admin / Manager only
Body Parameters
NameTypeRequiredDescription
product_idintegerrequiredProduct to transfer
from_warehouse_idintegerrequiredSource warehouse
to_warehouse_idintegerrequiredDestination warehouse
quantitynumberrequiredUnits to transfer
Response
{ "message": "Transfer complete", "quantity": 30 }

Orders
Create, update, and fulfill orders. Supports manual, API, POS, and channel-synced orders.
GET /api/orders List orders
Query Parameters
NameTypeRequiredDescription
statusstringoptionalpending | paid | fulfilling | fulfilled | shipped | cancelled
customer_idintegeroptionalFilter by customer
fromdateoptionalStart date (YYYY-MM-DD)
todateoptionalEnd date (YYYY-MM-DD)
Response
{
  "orders": [
    {
      "id":            1042,
      "order_number":  "ORD-1042",
      "customer_id":   42,
      "customer_name": "Acme Corp",
      "status":        "fulfilling",
      "channel":       "api",
      "total_amount":  "149.97",
      "item_count":    1,
      "warehouse_id":  1,
      "created_at":    "2026-05-06T09:00:00Z"
    }
  ],
  "total": 382,
  "page":  1,
  "limit": 50
}
GET /api/orders/:id Get an order
Returns full order detail including line items, fulfillment status, and customer info.
Response
{
  "id":           1042,
  "order_number": "ORD-1042",
  "customer_id":  42,
  "customer_name": "Acme Corp",
  "status":       "fulfilling",
  "channel":      "api",
  "total_amount": "149.97",
  "shipping_address": {
    "line1":    "123 George St",
    "city":     "Sydney",
    "state":    "NSW",
    "postcode": "2000",
    "country":  "AU"
  },
  "items": [
    {
      "id":                 2081,
      "product_id":        7,
      "sku":               "WGT-500",
      "product_name":     "Widget Pro 500",
      "quantity":          3,
      "unit_price":        "49.99",
      "quantity_fulfilled": 0
    }
  ],
  "created_at": "2026-05-06T09:00:00Z"
}
POST /api/orders Create an order
Body Parameters
NameTypeRequiredDescription
customer_idintegeroptionalCustomer reference
itemsarrayrequiredArray of { product_id, quantity, unit_price }
warehouse_idintegeroptionalFulfillment warehouse
channelenumoptionalmanual | api | pos | b2b
shipping_addressobjectoptionalShipping address JSON
Request body
{
  "customer_id": 42,
  "channel":     "api",
  "items": [
    { "product_id": 7, "quantity": 3, "unit_price": 49.99 }
  ]
}
Response — 201 Created
{
  "id":           1043,
  "order_number": "ORD-1043",
  "status":       "pending",
  "total_amount": "149.97",
  "created_at":   "2026-05-06T11:35:10Z"
}
PATCH /api/orders/:id/status Update order status
Transitions the order to a new status. Only valid transitions are allowed.
Request body
{ "status": "shipped" }
Response
{ "message": "Order status updated", "status": "shipped" }
POST /api/orders/:id/fulfill Fulfill an order
Admin / Manager only
Records fulfillment against line items and decrements inventory. Pass items array with order_item_id and quantity_fulfilled.
Response
{
  "message":        "Fulfillment recorded",
  "fulfilled_items": 1,
  "order_status":   "fulfilled"
}
GET /api/orders/stats Order statistics
Returns totals, revenue, and status breakdown for the current period.
Response
{
  "period":           "2026-05",
  "total_orders":     382,
  "total_revenue":    "28450.80",
  "avg_order_value":  "74.48",
  "by_status": {
    "pending":    14,
    "paid":       8,
    "fulfilling": 22,
    "fulfilled":  190,
    "shipped":    148
  }
}
GET /api/orders/export Export orders CSV
Downloads orders as CSV. Response is text/csv with Content-Disposition: attachment.

Customers
Manage customer records, addresses, and B2B price tiers.
GET /api/customers List customers
Returns paginated customer list. Supports search (name, company, email) and type filter.
Response
{
  "customers": [
    {
      "id":             42,
      "name":           "Acme Corp",
      "email":          "procurement@acme.com",
      "phone":          "+61 2 9000 0000",
      "company":        "Acme Corporation Pty Ltd",
      "customer_type":  "b2b",
      "total_orders":   18,
      "total_spent":    "4820.40",
      "created_at":     "2025-09-12T00:00:00Z"
    }
  ],
  "total": 94, "page": 1, "limit": 50
}
GET /api/customers/:id Get a customer
Returns customer detail including addresses and order history summary.
Response
{
  "id":            42,
  "name":          "Acme Corp",
  "email":         "procurement@acme.com",
  "company":       "Acme Corporation Pty Ltd",
  "customer_type": "b2b",
  "total_orders":  18,
  "total_spent":   "4820.40",
  "addresses": [
    {
      "id":         5,
      "type":       "shipping",
      "line1":      "123 George St",
      "city":       "Sydney",
      "state":      "NSW",
      "postcode":   "2000",
      "country":    "AU",
      "is_default": true
    }
  ],
  "created_at": "2025-09-12T00:00:00Z"
}
POST /api/customers Create a customer
Body Parameters
NameTypeRequiredDescription
namestringrequiredCustomer name
emailstringoptionalEmail address
phonestringoptionalPhone number
companystringoptionalCompany name
customer_typeenumoptionalretail | wholesale | b2b
Response — 201 Created
{
  "id":            95,
  "name":          "Acme Corp",
  "email":         "procurement@acme.com",
  "customer_type": "b2b",
  "created_at":    "2026-05-06T11:40:00Z"
}
POST /api/customers/:id/addresses Add an address
Adds a billing or shipping address to a customer record.
Response — 201 Created
{
  "id":          18,
  "customer_id": 42,
  "type":        "shipping",
  "line1":       "45 Pitt St",
  "city":        "Sydney",
  "state":       "NSW",
  "postcode":    "2000",
  "country":     "AU",
  "is_default":  false
}

Suppliers & Purchase Orders
Manage vendors and create purchase orders to replenish stock.
GET /api/suppliers List suppliers
Returns all suppliers for this tenant.
Response
{
  "suppliers": [
    {
      "id":              3,
      "name":            "FastenersPro Pty Ltd",
      "email":           "orders@fastenerspro.com.au",
      "phone":           "+61 3 9000 1234",
      "payment_terms":   "Net 30",
      "lead_time_days":  7,
      "product_count":   14
    }
  ],
  "total": 8
}
POST /api/suppliers Create a supplier
Admin / Manager only
Body Parameters
NameTypeRequiredDescription
namestringrequiredSupplier name
emailstringoptionalContact email
payment_termsstringoptionale.g. Net 30
Response — 201 Created
{
  "id":             9,
  "name":           "FastenersPro Pty Ltd",
  "email":          "orders@fastenerspro.com.au",
  "payment_terms":  "Net 30",
  "created_at":     "2026-05-06T12:00:00Z"
}
GET /api/suppliers/purchase-orders/all List purchase orders
Returns all purchase orders. Supports status and supplier_id filters.
Response
{
  "purchase_orders": [
    {
      "id":             88,
      "po_number":      "PO-0088",
      "supplier_id":    3,
      "supplier_name":  "FastenersPro Pty Ltd",
      "warehouse_id":   1,
      "status":         "partial",
      "total_amount":   "3240.00",
      "expected_date":  "2026-05-10",
      "created_at":     "2026-04-28T00:00:00Z"
    }
  ],
  "total": 21
}
POST /api/suppliers/purchase-orders Create a purchase order
Admin / Manager only
Creates a draft PO with line items. Pass supplier_id, warehouse_id, and items array.
Response — 201 Created
{
  "id":           89,
  "po_number":    "PO-0089",
  "status":       "draft",
  "total_amount": "1800.00",
  "created_at":   "2026-05-06T12:10:00Z"
}
POST /api/suppliers/purchase-orders/:id/receive Receive a PO
Admin / Manager only
Records received quantities against PO line items and increments inventory accordingly.
Response
{
  "message":          "Stock received",
  "received_lines":   3,
  "inventory_updated": true
}

Warehouses
Create and manage warehouse locations.
GET /api/warehouses List warehouses
Returns all warehouse locations for this tenant.
Response
{
  "warehouses": [
    {
      "id":          1,
      "name":        "Sydney Warehouse",
      "code":        "SYD",
      "address":     "10 Distribution Dr",
      "city":        "Ryde",
      "state":       "NSW",
      "is_default":  true,
      "total_skus":  48,
      "total_value": "62400.00"
    },
    {
      "id":          2,
      "name":        "Melbourne Warehouse",
      "code":        "MEL",
      "city":        "Dandenong",
      "state":       "VIC",
      "is_default":  false,
      "total_skus":  32,
      "total_value": "21920.50"
    }
  ]
}
POST /api/warehouses Create a warehouse
Admin only
Body Parameters
NameTypeRequiredDescription
namestringrequiredWarehouse name
codestringrequiredShort code (e.g. SYD)
addressstringoptionalStreet address
citystringoptionalCity
is_defaultbooleanoptionalSet as default warehouse
Response — 201 Created
{
  "id":         3,
  "name":       "Brisbane Warehouse",
  "code":       "BNE",
  "city":       "Rocklea",
  "state":      "QLD",
  "is_default": false,
  "created_at": "2026-05-06T12:20:00Z"
}

Batches / Lots
Track products by batch number, lot, manufacture date, and expiry date. Only available for products with tracking_type: batch.
GET /api/batches List batches
Returns all batches. Filter by product_id or expiring_days.
Response
{
  "batches": [
    {
      "id":               14,
      "product_id":       7,
      "sku":              "WGT-500",
      "product_name":    "Widget Pro 500",
      "batch_number":    "BTH-2026-041",
      "lot_number":      "L04-A",
      "manufacture_date": "2026-02-10",
      "expiry_date":     "2028-02-10",
      "quantity":         200,
      "notes":            null
    }
  ],
  "total": 38
}
GET /api/batches/expiring Expiring batches
Returns batches expiring within the next days (default: 30).
Response
{
  "batches": [
    {
      "id":                9,
      "product_id":        22,
      "sku":               "CHM-001",
      "product_name":     "Chemical Compound A",
      "batch_number":     "BTH-2026-009",
      "expiry_date":      "2026-05-28",
      "days_until_expiry": 22,
      "quantity":          45
    }
  ],
  "total": 3
}
POST /api/batches Create a batch
Admin / Manager only
Body Parameters
NameTypeRequiredDescription
product_idintegerrequiredProduct (must have batch tracking)
batch_numberstringrequiredUnique batch number
expiry_datedateoptionalExpiry date (YYYY-MM-DD)
manufacture_datedateoptionalManufacture date
quantitynumberoptionalInitial batch quantity
Response — 201 Created
{
  "id":              39,
  "product_id":      7,
  "batch_number":    "BTH-2026-042",
  "expiry_date":     "2028-03-01",
  "quantity":         500,
  "created_at":      "2026-05-06T12:30:00Z"
}

Serial Numbers
Track individual units by serial number. Only available for products with tracking_type: serial.
GET /api/serials List serial numbers
Returns serial numbers filtered by product_id, status, or warehouse_id.
Response
{
  "serials": [
    {
      "id":             201,
      "product_id":     31,
      "sku":            "EQP-PRO-X1",
      "product_name":  "ProX1 Equipment",
      "serial_number":  "SN-20260401-0001",
      "warehouse_id":   1,
      "warehouse_name": "Sydney",
      "status":         "in_stock",
      "created_at":     "2026-04-01T00:00:00Z"
    }
  ],
  "total": 84
}
POST /api/serials Create a serial number
Admin / Manager only
Response — 201 Created
{
  "id":            285,
  "product_id":    31,
  "serial_number": "SN-20260506-0001",
  "warehouse_id":  1,
  "status":        "in_stock",
  "created_at":    "2026-05-06T12:35:00Z"
}
POST /api/serials/bulk Bulk create serial numbers
Admin / Manager only
Create multiple serial numbers in one request. Pass product_id, warehouse_id, and serials array of strings.
Response — 201 Created
{
  "message": "Serial numbers created",
  "count":   10,
  "ids":     [286, 287, 288, 289, 290, 291, 292, 293, 294, 295]
}

Reports
Financial summaries, inventory valuation, COGS, and dashboard metrics.
GET /api/reports/dashboard Dashboard KPIs
Returns today's revenue, open orders, low stock count, and recent activity for the main dashboard.
Response
{
  "today": {
    "revenue":    4820.50,
    "orders":     12,
    "units_sold": 38
  },
  "open_orders":      44,
  "low_stock_count":  6,
  "out_of_stock_count": 2,
  "recent_orders": [
    {
      "id":            1042,
      "order_number":  "ORD-1042",
      "customer_name": "Acme Corp",
      "total_amount":  "149.97",
      "status":        "fulfilling",
      "created_at":    "2026-05-06T09:00:00Z"
    }
  ]
}
GET /api/reports/financial Financial summary
Revenue, COGS, and gross margin grouped by period. Supports from, to, and group_by (day/week/month).
Response
{
  "from": "2026-04-01",
  "to":   "2026-04-30",
  "summary": {
    "revenue":          28450.80,
    "cogs":             10842.30,
    "gross_margin":     17608.50,
    "gross_margin_pct": 61.9
  },
  "periods": [
    { "period": "2026-04-01", "revenue": 920.00, "cogs": 350.40, "gross_margin": 569.60 }
  ]
}
GET /api/reports/inventory Inventory valuation
Stock on hand valued at cost price, broken down by product and warehouse.
Response
{
  "valuation_date": "2026-05-06",
  "total_value":    84320.50,
  "items": [
    {
      "product_id":     7,
      "sku":            "WGT-500",
      "product_name":  "Widget Pro 500",
      "warehouse_id":   1,
      "warehouse_name": "Sydney",
      "quantity":        200,
      "cost_price":     "18.00",
      "total_value":    3600.00
    }
  ],
  "total": 48
}
GET /api/reports/cogs Cost of goods sold
COGS report for a date range. Supports same filters as financial report.
Response
{
  "from":       "2026-04-01",
  "to":         "2026-04-30",
  "total_cogs": 10842.30,
  "items": [
    {
      "product_id":   7,
      "sku":          "WGT-500",
      "product_name": "Widget Pro 500",
      "units_sold":   82,
      "cost_price":   "18.00",
      "total_cogs":   1476.00
    }
  ]
}

API Keys
Create and manage API keys for programmatic access. Keys are only shown once at creation.
GET /api/api-keys List API keys
Returns all API keys for this tenant. The full key value is never returned after creation.
Response
{
  "api_keys": [
    {
      "id":           3,
      "name":         "Shopify Connector",
      "key_prefix":   "iom_live_abc1",
      "permissions":  ["read", "write"],
      "last_used_at": "2026-05-06T08:42:00Z",
      "expires_at":   null,
      "is_active":    true,
      "created_at":   "2026-01-10T00:00:00Z"
    }
  ]
}
POST /api/api-keys Create an API key
Body Parameters
NameTypeRequiredDescription
namestringrequiredDescriptive label
permissionsarrayoptional["read"] or ["read","write"]
expires_atdateoptionalExpiry date (YYYY-MM-DD)
The full API key is returned only once in the creation response. Store it securely.
Response — 201 Created
{
  "id":          4,
  "name":        "Shopify Connector",
  "key":         "iom_live_abc123xyz789fullkeyshownonce",
  "permissions": ["read", "write"],
  "expires_at":  null,
  "created_at":  "2026-05-06T12:45:00Z"
}
DELETE /api/api-keys/:id Revoke an API key
Immediately revokes the key. Any requests using this key will receive 401.
Response
{ "message": "API key revoked" }
GET /api/api-keys/logs/all API usage logs
Returns a log of all requests made with API keys, including endpoint, status code, and response time.
Response
{
  "logs": [
    {
      "id":               4821,
      "api_key_id":       3,
      "key_name":         "Shopify Connector",
      "method":           "GET",
      "path":             "/api/external/products",
      "status_code":      200,
      "response_time_ms": 14,
      "ip_address":       "203.0.113.5",
      "created_at":       "2026-05-06T08:42:00Z"
    }
  ],
  "total": 4821
}

External API
The external API is designed for third-party integrations. It uses API key authentication only (no JWT) and is available at /api/external.
All external endpoints require an API key with the appropriate permission scope. Pass as X-API-Key: iom_live_xxxx.
GET /api/external/products List products
Returns active products with SKU, price, and stock levels. Requires read permission.
Response
{
  "products": [
    {
      "id":          7,
      "name":        "Widget Pro 500",
      "sku":         "WGT-500",
      "price":       "49.99",
      "barcode":     "9312345678901",
      "vendor":      "WidgetCo",
      "total_stock": 320,
      "is_active":   true
    }
  ],
  "total": 48
}
GET /api/external/products/:sku Get product by SKU
Looks up a product by its SKU. Returns current price and stock level.
Response
{
  "id":      7,
  "name":    "Widget Pro 500",
  "sku":     "WGT-500",
  "price":   "49.99",
  "barcode": "9312345678901",
  "stock": [
    { "warehouse_id": 1, "warehouse_name": "Sydney",    "quantity": 200 },
    { "warehouse_id": 2, "warehouse_name": "Melbourne", "quantity": 120 }
  ]
}
GET /api/external/inventory Stock levels
Returns current on-hand quantities for all products across all warehouses.
Response
{
  "inventory": [
    {
      "product_id":    7,
      "sku":           "WGT-500",
      "product_name":  "Widget Pro 500",
      "warehouse_id":  1,
      "warehouse_name": "Sydney",
      "quantity":       200
    }
  ],
  "total": 156
}
GET /api/external/orders List orders
Returns orders. Supports status, from, to filters.
Response
{
  "orders": [
    {
      "id":           1042,
      "order_number": "ORD-1042",
      "status":       "fulfilling",
      "channel":      "api",
      "total_amount": "149.97",
      "created_at":   "2026-05-06T09:00:00Z"
    }
  ],
  "total": 382
}
POST /api/external/orders Create an order
Creates an order programmatically. Requires write permission. Same body as POST /api/orders.
Request
POST /api/external/orders
X-API-Key: iom_live_abc123
Content-Type: application/json

{
  "channel": "api",
  "items": [
    { "product_id": 12, "quantity": 2, "unit_price": 49.99 }
  ]
}
Response — 201 Created
{
  "id":           1043,
  "order_number": "ORD-1043",
  "status":       "pending",
  "total_amount": "99.98",
  "created_at":   "2026-05-06T12:50:00Z"
}
POST /api/external/inventory/sync Sync inventory
Push inventory updates from an external system. Requires write permission. Pass an array of { sku, warehouse_id, quantity } to set absolute quantities.
Response
{
  "message":  "Inventory synced",
  "updated":  3,
  "skipped":  0
}
GET /api/external/customers List customers
Returns customer records. Requires read permission.
Response
{
  "customers": [
    {
      "id":            42,
      "name":          "Acme Corp",
      "email":         "procurement@acme.com",
      "company":       "Acme Corporation Pty Ltd",
      "customer_type": "b2b"
    }
  ],
  "total": 94
}