ScanMan Hive Documentation

A Frappe-native warehouse management system purpose-built for furniture and textile manufacturers. Rule-based today, robot-ready tomorrow.

ScanMan Hive runs as a standalone Frappe app — no hard ERPNext dependency. Install it alongside an existing ERPNext site to sync stock movements into Stock Entry / Delivery Note / Purchase Receipt, or run it on its own with its own item and stock ledger. The REST API is the integration surface for scanner PWAs, hardware handhelds, and — at the Operator maturity stage — ground AGVs and aerial drone swarms.

Why "Hive"?

A hive is many actuators following shared rules and producing collective intelligence. Humans with phones, ground bots, aerial bots, and fixed ceiling scanners are all members of the hive. One event stream, one rules engine, many bodies. The same endpoint a picker's phone hits today is the endpoint a drone hits tomorrow — the bodies are interchangeable.

Getting Started

Prerequisites

You need a working Frappe bench environment. The easiest path is a Frappe Cloud trial site; the most flexible is a local Docker bench.

RequirementVersionNotes
Frappe Framework>= 15.0.0v14 may work but is not tested
Python>= 3.10Matches Frappe v15 baseline
MariaDB / MySQL10.6+Standard Frappe dependency
Node.js18+For bench build steps
ERPNextoptionalOnly required if you want automatic Stock Entry sync

Install

From your bench root:

# 1. Pull the app into the bench
cd $BENCH_ROOT
bench get-app scanman_hive https://github.com/<your-org>/scanman_hive

# 2. Install on a site
bench --site <sitename> install-app scanman_hive

# 3. Migrate (runs schema creation for the 9 DocTypes)
bench --site <sitename> migrate

After the migrate step, open Frappe Desk on that site. You should see a new Scanman Hive module in the desk, containing WMS Warehouse, WMS Zone, WMS Location, WMS Item, WMS Stock Balance, WMS Transaction, WMS Pick List, WMS Pick List Item and WMS Putaway Rule.

Load the Acacia Interiors demo dataset

The demo dataset models a fictional Cape Town furniture and textile manufacturer. Loading it takes about 10 seconds.

bench --site <sitename> execute scanman_hive.setup.acacia.install_demo_data

You should see:

✓ Acacia Interiors demo data installed.

This creates 1 warehouse, 4 zones, 24 locations, 25 items across 7 families, 4 putaway rules, and 13 initial stock rows including dye-lot IDs on fabric rolls. See Demo Data → Acacia Interiors for the full inventory.

Create an API key

ScanMan Hive uses Frappe's standard API key + secret authentication. Every request carries a header of the form Authorization: token <api_key>:<api_secret>.

  1. Open Frappe Desk → User → {your user}
  2. Scroll to Settings → API Access
  3. Click Generate Keys — the secret is shown once
  4. Store the pair in your shell:
export SCANMAN_URL=https://dev.scanman-hive.2nth.ai
export SCANMAN_AUTH="token xxxxxxxxxxxxxxx:yyyyyyyyyyyyyyy"
Treat secrets like passwords

API keys carry the full permissions of the user they belong to. Rotate if leaked, and never commit them to git.

Your first API call

Look up a fabric item from the Acacia dataset:

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.lookup_item" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"code": "FAB-DIV-CH"}'

Expected response:

{
  "message": {
    "ok": true,
    "item": {
      "name": "FAB-DIV-CH",
      "item_code": "FAB-DIV-CH",
      "item_name": "Divina Charcoal Fabric",
      "item_family": "Fabric",
      "stock_type": "Raw",
      "uom": "m",
      "tracks_lot": 1
    }
  }
}

The message wrapper is Frappe's convention — the actual payload is nested inside. If you see this response, your install is healthy and you're ready to run the full demo flow.

Architecture

Three layers, one source of truth

The stack splits cleanly into three layers. Each has a narrow job and a clean contract with the others. Nothing is probabilistic.

System of Record
ERPNext (optional) + ScanMan Hive
Customers, Items, BOMs, Sales Orders, Stock Balances, Transactions. Frappe Framework, MariaDB. All transactional truth lives here. ERPNext is optional — ScanMan Hive works standalone.
Physical Layer
ScanMan WMS + Shop Floor
Barcode-driven receiving, put-away, picking, cycle counting, and shop-floor Job Card execution. Handhelds, workstation tablets, and — at the Operator stage — ground AGVs and aerial drones. Every scan writes to the same WMS Transaction table.
Edge Platform
Cloudflare
Workers for API glue. Tunnel for private Frappe access. R2 for files. D1 for read-optimised dashboards. Pages for the sales and exec front ends. Zero Trust for identity. All running at Cloudflare's Johannesburg and Cape Town POPs.

The nine DocTypes

Nine DocTypes cover the entire warehouse domain. Every one includes TrackChanges for audit history, and every stock-mutating call writes an immutable WMS Transaction row plus adjusts one or two WMS Stock Balance rows.

DocTypeAutonamePurpose
WMS Warehousefield:warehouse_codePhysical site with optional ERPNext Warehouse link
WMS Zonefield:zone_codeLogical grouping: Receiving · Raw Stores · Production · Finished Goods · Dispatch · Quarantine
WMS Locationfield:location_codeScannable bin, rack, bench, or staging position
WMS Itemfield:item_codeItem master with 7 families, 3 stock types (Raw/WIP/Finished), lot + serial flags
WMS Stock BalancehashReal-time qty at a location with lot tracking and last-scanned audit
WMS TransactionSMTX-.YYYY.-.#####Immutable scan audit trail — 10 transaction types, device type per row
WMS Pick ListSHPL-.YYYY.-.#####Zone-sorted pick path from a sales or work order reference
WMS Pick List Itemchild tableOne row per picked line
WMS Putaway Rulefield:rule_nameFamily + velocity-based zone assignment with priority ordering

Data flow

A raw material moves through three stock states during its life inside the factory. ScanMan Hive tracks each transition.

// Raw → WIP → Finished
Receive     Raw Stores
   
Putaway     Raw bin assigned by putaway rule
   
Kit         Production bench (WIP-CUT, WIP-UPH, ...)
   
WIP-Move    Next workstation in routing
   
FG-Receipt  Finished Goods rack
   
Pick        Outbound staging
   
Pack        Verified against pick list
   
Ship        Out the door
// Cycle Count can happen at any state to reconcile reality vs ledger

Transaction types

Every scan writes a WMS Transaction row with one of these types:

Receive

Incoming material booked into a receiving location.

Putaway

Movement from receiving to a raw-stores location.

Kit

Raw material issued to a production bench against a work order.

WIP-Move

Subassembly moving between workstations during production.

FG-Receipt

Finished good booked back into stock from production.

Pick

Outbound picking against a pick list line.

Pack

Pick list verified and consolidated for dispatch.

Ship

Final out-the-door transaction, closing a sales order delivery.

Count

Cycle count result — compared to balance and adjusted if different.

Adjust

Manual stock adjustment (write-off, damage, found stock).

API Reference

All endpoints are JSON in / JSON out, exposed via Frappe's standard /api/method/scanman_hive.api.<function> route.

Authentication & base URL

Every request carries an API key + secret in the Authorization header:

Authorization: token <api_key>:<api_secret>

Set base URL and auth once in your shell:

export SCANMAN_URL=https://dev.scanman-hive.2nth.ai
export SCANMAN_AUTH="token xxxxxxxxxxxxxxx:yyyyyyyyyyyyyyy"

Response format

Success responses are wrapped in Frappe's standard message envelope:

{ "message": { "ok": true, "transaction": "SMTX-2026-00042" } }

Errors follow Frappe's standard error response with an exc_type and a human-readable _server_messages field.

Lookup endpoints

POSTscanman_hive.api.lookup_item

Find a WMS Item by item code or barcode.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.lookup_item" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"code": "FAB-DIV-CH"}'

POSTscanman_hive.api.lookup_location

Find a WMS Location by location code.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.lookup_location" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"code": "RAW-A01-01"}'

POSTscanman_hive.api.stock_at_location

Return all non-zero stock balances at a location.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.stock_at_location" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"location": "RAW-A01-01"}'

Receive

POSTscanman_hive.api.receive_item

Register incoming stock at a receiving location. Writes a WMS Transaction of type Receive and creates or increments a WMS Stock Balance row.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.receive_item" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "item": "FAB-DIV-CH",
    "qty": 120,
    "location": "RCV-01",
    "lot_no": "DYE-2026-058",
    "reference_doctype": "Purchase Receipt",
    "reference_name": "PREC-0042"
  }'

Response: { "ok": true, "transaction": "SMTX-2026-00042", "location": "RCV-01" }

Putaway

POSTscanman_hive.api.suggest_putaway

Resolve the highest-priority matching putaway rule for an item, then return the first active location in its preferred zone.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.suggest_putaway" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"item": "FAB-DIV-CH"}'

Response: { "ok": true, "rule": "Fast-moving fabric → near Cut bench", "zone": "ACACIA-CPT-RAW", "location": "RAW-A01-01" }

POSTscanman_hive.api.confirm_putaway

Commit the move from receiving to a putaway location. Operator can override the suggestion.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.confirm_putaway" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "item": "FAB-DIV-CH",
    "qty": 120,
    "from_location": "RCV-01",
    "to_location": "RAW-A01-01",
    "lot_no": "DYE-2026-058"
  }'

Kit to production

POSTscanman_hive.api.confirm_kit

Issue a raw material from raw stores to a production bench against a work order. Writes a Kit transaction linked to the work order reference.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.confirm_kit" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "work_order": "WO-2026-0017",
    "item": "FAB-DIV-CH",
    "qty": 8,
    "from_location": "RAW-A01-01",
    "to_location": "WIP-UPH",
    "lot_no": "DYE-2026-058"
  }'

Pick / Pack / Ship

POSTscanman_hive.api.create_pick_list

Create a pick list from a sales or work order reference. Lines are sequenced by zone → aisle → bay → level — a simple deterministic path sort, not TSP-optimised.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.create_pick_list" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "warehouse": "ACACIA-CPT",
    "reference_doctype": "Sales Order",
    "reference_name": "SAL-ORD-2026-0042",
    "items": [
      { "item": "FG-CBOX-STD", "qty": 3 },
      { "item": "FG-DESK-STD", "qty": 6 }
    ]
  }'

POSTscanman_hive.api.confirm_pick

Record a pick against a specific pick list line. Short-picks are accepted and logged as Short.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.confirm_pick" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "pick_list": "SHPL-2026-00007",
    "item": "FG-CBOX-STD",
    "qty": 3,
    "location": "FG-A01-01"
  }'

POSTscanman_hive.api.confirm_pack

Pack-verify every picked line. Requires the pick list to be in Picked status.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.confirm_pack" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"pick_list": "SHPL-2026-00007"}'

POSTscanman_hive.api.confirm_ship

Mark a pick list shipped. Writes one Ship transaction per line.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.confirm_ship" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"pick_list": "SHPL-2026-00007"}'

Cycle count

POSTscanman_hive.api.confirm_count

Record a cycle count result. Compared to the current balance; a delta triggers an automatic adjustment. Returns previous qty, counted qty, and delta.

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.confirm_count" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "item": "FAB-DIV-CH",
    "location": "RAW-A01-01",
    "counted_qty": 118,
    "lot_no": "DYE-2026-047"
  }'

Generic scan event — the robot-ready endpoint

One polymorphic endpoint that a picker's phone, a ground AGV, or an aerial drone can all hit with the same schema. This is the punchline for the robot-ready demo: same protocol, different bodies.

POSTscanman_hive.api.scan_event

Dispatches to the specific handler based on event.type. The device_type field is preserved on the resulting WMS Transaction row — that's how the audit trail distinguishes human scans from robot scans.

Human on a phone

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.scan_event" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "event": {
      "type": "Count",
      "item": "FAB-DIV-CH",
      "qty": 118,
      "to_location": "RAW-A01-01",
      "lot_no": "DYE-2026-047",
      "device_type": "Phone"
    }
  }'

Aerial drone — same endpoint, same schema

curl -X POST "$SCANMAN_URL/api/method/scanman_hive.api.scan_event" \
  -H "Authorization: $SCANMAN_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "event": {
      "type": "Count",
      "item": "FAB-DIV-CH",
      "qty": 118,
      "to_location": "RAW-A01-01",
      "lot_no": "DYE-2026-047",
      "device_type": "Aerial Robot"
    }
  }'
Supported event types

Currently: Receive, Putaway, Kit, Count. More types are dispatched via the same endpoint as the app grows — phone, ground robot, and aerial robot clients all remain unchanged.

Error codes

HTTPWhen
200Success. Payload in message.
401Missing or invalid Authorization header.
403Valid token but the user lacks role permission on the DocType.
417frappe.throw() — business rule violation. Human-readable message in _server_messages.
500Unhandled exception.

Demo Data

Acacia Interiors

Acacia Interiors is a fictional Cape Town furniture and textile manufacturer used to showcase ScanMan Hive end-to-end. It models the three-state stock problem that real furniture and textile manufacturers face: raw material → WIP → finished goods, with lot tracking on fabrics.

AttributeValue
Warehouse codeACACIA-CPT
Address12 Paarden Eiland Road, Cape Town, 7405
Zones4
Locations24
Items25 across 7 families
Putaway rules4
Opening stock rows13 (incl. dye-lot IDs on fabric)

Warehouse layout

Four zones map directly to the manufacturing flow:

Zone CodeNameTypeLocations
ACACIA-CPT-RCVReceiving DockReceiving3 staging (RCV-01..03)
ACACIA-CPT-RAWRaw StoresRaw Stores10 racks + bins (RAW-A01..C02)
ACACIA-CPT-WIPProduction / WIPProduction6 workstation benches (Cut, Sew, Upholstery, Assembly, Finishing, QC)
ACACIA-CPT-FGFinished GoodsFinished Goods5 floor + rack positions (FG-01..02, FG-A01..A02)

Items catalogue

25 items across 7 families cover the configurable options a furniture manufacturer actually touches:

Fabric (4) — lot-tracked textile

CodeNameUOMVelocity
FAB-DIV-CHDivina Charcoal FabricmA
FAB-DIV-NVDivina Navy FabricmB
FAB-DIV-OLDivina Olive FabricmB
FAB-HAL-GRHallingdal Grey FabricmC

Laminate (3) — sheet stock

LAM-WOAKWhite Oak Laminate 18mmSheetA
LAM-BLOAKBlack Oak Laminate 18mmSheetB
LAM-WALNWalnut Laminate 18mmSheetC

Foam (2), Frames (3), Hardware (4)

FOAM-HDHigh-Density Foam 50mmSheetA
FOAM-MDMedium-Density Foam 30mmSheetB
FRM-POD-SPod Frame SmallPieceA
FRM-POD-LPod Frame LargePieceB
FRM-DESKDesk Frame StandardPieceB
HW-BOLT-M8M8 Bolt 40mmPieceA
HW-HING-AAcoustic Hinge APieceB
HW-CASTORCastor Wheel 75mmPieceB
HW-GLAZEGlass Panel 10mm ClearPieceA

Subassemblies (2) — WIP

SUB-POD-UPUpholstered Pod PanelPieceB
SUB-DESK-TDesk Top FinishedPieceB

Finished Goods (7)

FG-CBOX-STDChatterBox Classic StandardPieceA
FG-CBOX-PREMChatterBox Classic PremiumPieceB
FG-CPOD-STDCitiPod StandardPieceA
FG-CPOD-GLASSCitiPod Glass CeilingPieceC
FG-DESK-STDUrban Workbench DeskPieceA
FG-DESK-SITSit-Stand DeskPieceB
FG-BOOTH-4AirSpace 4-person BoothPieceC

Putaway rules

Four active rules drive putaway suggestions. Lower priority number = higher priority.

PriorityNameFamilyVelocityPreferred Zone
5Finished goods → FG zoneFinished GoodAnyACACIA-CPT-FG
10Fast-moving fabric → near Cut benchFabricAACACIA-CPT-RAW
15Hardware fast-movers → pick-faceHardwareAACACIA-CPT-RAW
20Laminate sheets → main racksLaminateAnyACACIA-CPT-RAW

Roadmap

The Maturity Model — User · Builder · Operator

ScanMan Hive is positioned for the whole journey, not just today. The architecture doesn't need to change between stages — the same event contracts carry more intelligence on top.

User stage — today

Rule-based foundation. Humans scan, rules dispatch work, Frappe is the source of truth. This is the solid ground everything else stands on. Day 1–10 of the build lives here.

Builder stage — next

Model-assisted. AI helps with slotting, pick-path learning, exception triage, and anomaly detection — always piggybacking on a rule-based path that already works, so the fallback is "do what we did yesterday."

Operator stage — future

Robot-ready. The same event contracts drive ground AGVs and aerial drone swarms. Humans handle exceptions; the hive handles routine. scan_event is the integration point that's already live today.

Rule-based today. Robot-ready tomorrow.

If your scan event stream is designed for one picker today, it's designed for a hundred robots tomorrow. The architecture is already shaped to accept an AI layer — adding one is a change of inputs, not a re-platform.