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.
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.
| Requirement | Version | Notes |
|---|---|---|
| Frappe Framework | >= 15.0.0 | v14 may work but is not tested |
| Python | >= 3.10 | Matches Frappe v15 baseline |
| MariaDB / MySQL | 10.6+ | Standard Frappe dependency |
| Node.js | 18+ | For bench build steps |
| ERPNext | optional | Only 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>.
- Open Frappe Desk → User → {your user}
- Scroll to Settings → API Access
- Click Generate Keys — the secret is shown once
- Store the pair in your shell:
export SCANMAN_URL=https://dev.scanman-hive.2nth.ai
export SCANMAN_AUTH="token xxxxxxxxxxxxxxx:yyyyyyyyyyyyyyy"
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.
WMS Transaction table.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.
| DocType | Autoname | Purpose |
|---|---|---|
| WMS Warehouse | field:warehouse_code | Physical site with optional ERPNext Warehouse link |
| WMS Zone | field:zone_code | Logical grouping: Receiving · Raw Stores · Production · Finished Goods · Dispatch · Quarantine |
| WMS Location | field:location_code | Scannable bin, rack, bench, or staging position |
| WMS Item | field:item_code | Item master with 7 families, 3 stock types (Raw/WIP/Finished), lot + serial flags |
| WMS Stock Balance | hash | Real-time qty at a location with lot tracking and last-scanned audit |
| WMS Transaction | SMTX-.YYYY.-.##### | Immutable scan audit trail — 10 transaction types, device type per row |
| WMS Pick List | SHPL-.YYYY.-.##### | Zone-sorted pick path from a sales or work order reference |
| WMS Pick List Item | child table | One row per picked line |
| WMS Putaway Rule | field:rule_name | Family + 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"
}
}'
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
| HTTP | When |
|---|---|
200 | Success. Payload in message. |
401 | Missing or invalid Authorization header. |
403 | Valid token but the user lacks role permission on the DocType. |
417 | frappe.throw() — business rule violation. Human-readable message in _server_messages. |
500 | Unhandled 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.
| Attribute | Value |
|---|---|
| Warehouse code | ACACIA-CPT |
| Address | 12 Paarden Eiland Road, Cape Town, 7405 |
| Zones | 4 |
| Locations | 24 |
| Items | 25 across 7 families |
| Putaway rules | 4 |
| Opening stock rows | 13 (incl. dye-lot IDs on fabric) |
Warehouse layout
Four zones map directly to the manufacturing flow:
| Zone Code | Name | Type | Locations |
|---|---|---|---|
ACACIA-CPT-RCV | Receiving Dock | Receiving | 3 staging (RCV-01..03) |
ACACIA-CPT-RAW | Raw Stores | Raw Stores | 10 racks + bins (RAW-A01..C02) |
ACACIA-CPT-WIP | Production / WIP | Production | 6 workstation benches (Cut, Sew, Upholstery, Assembly, Finishing, QC) |
ACACIA-CPT-FG | Finished Goods | Finished Goods | 5 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
| Code | Name | UOM | Velocity |
|---|---|---|---|
FAB-DIV-CH | Divina Charcoal Fabric | m | A |
FAB-DIV-NV | Divina Navy Fabric | m | B |
FAB-DIV-OL | Divina Olive Fabric | m | B |
FAB-HAL-GR | Hallingdal Grey Fabric | m | C |
Laminate (3) — sheet stock
LAM-WOAK | White Oak Laminate 18mm | Sheet | A |
LAM-BLOAK | Black Oak Laminate 18mm | Sheet | B |
LAM-WALN | Walnut Laminate 18mm | Sheet | C |
Foam (2), Frames (3), Hardware (4)
FOAM-HD | High-Density Foam 50mm | Sheet | A |
FOAM-MD | Medium-Density Foam 30mm | Sheet | B |
FRM-POD-S | Pod Frame Small | Piece | A |
FRM-POD-L | Pod Frame Large | Piece | B |
FRM-DESK | Desk Frame Standard | Piece | B |
HW-BOLT-M8 | M8 Bolt 40mm | Piece | A |
HW-HING-A | Acoustic Hinge A | Piece | B |
HW-CASTOR | Castor Wheel 75mm | Piece | B |
HW-GLAZE | Glass Panel 10mm Clear | Piece | A |
Subassemblies (2) — WIP
SUB-POD-UP | Upholstered Pod Panel | Piece | B |
SUB-DESK-T | Desk Top Finished | Piece | B |
Finished Goods (7)
FG-CBOX-STD | ChatterBox Classic Standard | Piece | A |
FG-CBOX-PREM | ChatterBox Classic Premium | Piece | B |
FG-CPOD-STD | CitiPod Standard | Piece | A |
FG-CPOD-GLASS | CitiPod Glass Ceiling | Piece | C |
FG-DESK-STD | Urban Workbench Desk | Piece | A |
FG-DESK-SIT | Sit-Stand Desk | Piece | B |
FG-BOOTH-4 | AirSpace 4-person Booth | Piece | C |
Putaway rules
Four active rules drive putaway suggestions. Lower priority number = higher priority.
| Priority | Name | Family | Velocity | Preferred Zone |
|---|---|---|---|---|
5 | Finished goods → FG zone | Finished Good | Any | ACACIA-CPT-FG |
10 | Fast-moving fabric → near Cut bench | Fabric | A | ACACIA-CPT-RAW |
15 | Hardware fast-movers → pick-face | Hardware | A | ACACIA-CPT-RAW |
20 | Laminate sheets → main racks | Laminate | Any | ACACIA-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.
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.