Skip to main content

Taxonomy ID System

Switchyard uses an 18-digit taxonomy ID system as a scannable, human-readable alternative to UUIDs. Every entity in the system has both a UUID (for internal database operations) and a taxonomy ID (for scanning, display, and external references).

Overview

Taxonomy IDs are designed to be:
  • Scannable - Can be printed as Code 128 barcodes
  • Checksum-protected - MOD-97 validation prevents typos
  • Type-aware - First digits identify the entity type
  • Globally unique - No collisions across entity types
All V1 API endpoints accept both UUIDs and taxonomy IDs as the :id parameter. The system automatically detects and resolves the correct format.

ID Format (Memory Map)

An 18-digit taxonomy ID follows this structure:
OffsetWidthFieldAllowedDescription
02VER01Schema version. Reserve 90-99 for future versions
22TYPESee table belowEntity type. Partitions the ID space
43FAC000999Facility/site code. Single site uses 000
77SEQ00000009999999Per-TYPE per-FAC counter
142RSV0099Reserved for future use (flags, subtypes). Keep 00
162CS0096MOD-97 checksum over digits 0–15
Total: 18 digits (even number, ideal for Code 128 Set C barcodes)

Type Allocations

Each entity type has a unique 2-digit TYPE code:
TYPEEntityDatabase TableDescription
10TotetotesContainer for order items
11RobotrobotsDelivery robot
13Productsellable_productsSellable product in the catalog
14SweepsweepsWarehouse sweep operations
20CartcartsPicking cart (holds up to 3 totes)
21BagbagsOrder bag (ambient/cold/frozen)
22Inventory Groupinventory_groupsWarehouse location/slot
23StaffstaffEmployee
24Retailer Locationretailer_locationsStore/retailer location
25Portalpickup_portalsPickup portal
27Manifestpartner_manifestsPartner brand shipment manifest
5089ReservedLot tracking, vendor/batch use
9099Future SchemaReserved for schema evolution
Type codes 12, 1519, 26, and 2849 are currently unassigned and available for future entity types.

Entity Type Details

Physical containers used to hold items during fulfillment. Uses tote_code column in the totes table.Use cases:
  • Order picking containers
  • Receiving containers
  • Inter-zone transfer bins
Seeded: 200 totes (SEQ 100001–100200)Example: 01 10 000 0100001 00 CS → Tote #100001
Autonomous delivery robots for warehouse operations. Primary key is the taxonomy ID in the robots table.Use cases:
  • Delivery robots
  • Picking assistants
Seeded: 12 robots (SEQ 100001–100012)Example: 01 11 000 0100001 00 CS → Robot #100001
Sellable products in the catalog. Has a taxonomy_id column on the sellable_products table.Use cases:
  • Product lookup
  • Inventory tracking
  • Order line items
Example: 01 13 000 0100001 00 CS → Product #100001
Warehouse sweep operations for inventory management. Has a taxonomy_id column on the sweeps table.Use cases:
  • Inventory sweep tracking
  • Cycle counts
Example: 01 14 000 0100001 00 CS → Sweep #100001
Picking carts that hold up to 3 totes. Primary key is the taxonomy ID in the carts table. Each cart has a color+animal identification name.Use cases:
  • Order picking workflows
  • Tote assignment
Seeded: 10 carts (SEQ 100001–100010)Example: 01 20 000 0100001 00 CS → Cart #100001
Order bags with temperature zone support (ambient, cold, frozen). Uses bag_code column in the bags table.Use cases:
  • Order packaging
  • Temperature zone segregation
Seeded: 1,000 test bags (SEQ 1–1000) + 5,000 production bags (SEQ 100001–105000)Example: 01 21 000 0100001 00 CS → Bag #100001
Warehouse locations and inventory slots. Has a taxonomy_id column on the inventory_groups table.Use cases:
  • Warehouse location tracking
  • Slot/bin identification
  • Zone management
Example: 01 22 000 0100001 00 CS → Inventory Group #100001
Employees and warehouse staff. Has a taxonomy_id column on the staff table.Use cases:
  • Staff identification
  • Task assignment
  • Access control
Example: 01 23 000 0100001 00 CS → Staff #100001
Store and retailer locations. Has a taxonomy_id column on the retailer_locations table.Use cases:
  • Location identification
  • Multi-store operations
Example: 01 24 000 0100001 00 CS → Retailer Location #100001
Pickup portals for order collection. Has a taxonomy_id column on the pickup_portals table.Use cases:
  • Customer pickup points
  • Portal scanning
Example: 01 25 000 0100001 00 CS → Portal #100001
Partner brand shipment manifests. Has a taxonomy_id column on the partner_manifests table. Used for QR code scanning of incoming partner shipments.Use cases:
  • Partner shipment receiving
  • QR code scanning
  • Manifest tracking
Example: 01 27 000 0100001 00 CS → Manifest #100001

SEQ Reservation Bands

Within each TYPE×FAC combination, the 7-digit SEQ space is divided:
SEQ RangePurpose
00000000099999Test/Temporary - Development and testing
01000008999999Production - Live working pool (~8.9M IDs)
90000009999999Future/Expansion - Reserved for growth
This gives each TYPE×FAC up to 8.9 million production IDs, which is more than sufficient for operational scale.

Checksum Calculation

The 2-digit checksum (CS) at positions 16-17 is calculated using MOD-97:
CS = (numeric value of first 16 digits) mod 97
Zero-pad to 2 digits if needed.

Examples (FAC=000, RSV=00)

EntityBase (first 16 digits)CSComplete Taxonomy ID
Tote #10000101100000100001000110000010000100 mod 9718-digit result
Robot #10000101110000100001000111000010000100 mod 9718-digit result
Product #10000101130000100001000113000010000100 mod 9718-digit result
Cart #10000101200000100001000120000010000100 mod 9718-digit result
Bag #10000101210000100001000121000010000100 mod 9718-digit result
Staff #10000101230000100001000123000010000100 mod 9718-digit result
Manifest #10000101270000100001000127000010000100 mod 9718-digit result
The checksum provides typo detection. If a user manually enters an ID incorrectly, the checksum validation will fail.

Database Implementation

Taxonomy IDs are automatically generated by database triggers when new entities are created.

Table Structure

Each table with taxonomy IDs has:
-- Primary key (UUID)
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

-- Taxonomy ID (auto-generated, unique)
taxonomy_id TEXT UNIQUE NOT NULL,

Generation Trigger

The set_taxonomy_id() trigger function runs on INSERT for supported tables and auto-generates a taxonomy ID if one is not already provided:
CREATE OR REPLACE FUNCTION set_taxonomy_id()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
  v_type_code TEXT;
BEGIN
  -- Determine type code based on table name
  CASE TG_TABLE_NAME
    WHEN 'sellable_products' THEN v_type_code := '13';
    WHEN 'sweeps' THEN v_type_code := '14';
    WHEN 'inventory_groups' THEN v_type_code := '22';
    WHEN 'staff' THEN v_type_code := '23';
    WHEN 'retailer_locations' THEN v_type_code := '24';
    WHEN 'pickup_portals' THEN v_type_code := '25';
    WHEN 'partner_manifests' THEN v_type_code := '27';
    ELSE
      RAISE EXCEPTION 'Unknown table for taxonomy ID trigger: %', TG_TABLE_NAME;
  END CASE;

  -- Only set if not already provided
  IF NEW.taxonomy_id IS NULL THEN
    NEW.taxonomy_id := get_next_taxonomy_id(v_type_code);
  END IF;

  RETURN NEW;
END;
$$;
Totes, robots, carts, and bags use their taxonomy ID as a primary key or dedicated code column (tote_code, bag_code) rather than the trigger-based approach. Their IDs are generated during table seeding.

API Usage

Dual ID Support

All V1 API endpoints accept both UUIDs and taxonomy IDs:
# Using UUID
GET /v1/products/550e8400-e29b-41d4-a716-446655440000

# Using Taxonomy ID
GET /v1/products/011300000012345042

ID Resolution

The API automatically detects the ID format and resolves taxonomy IDs to UUIDs for database queries:
// In route handlers
const id = await resolveId('sellable_products', idParam)
// Returns UUID regardless of input format
Tables currently supported by the resolveId() utility:
  • sellable_products
  • staff
  • inventory_groups
  • sweeps
  • retailer_locations

Detection Logic

function isTaxonomyId(id: string): boolean {
  // Taxonomy IDs are exactly 18 digits
  return /^\d{18}$/.test(id)
}

function isUUID(id: string): boolean {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)
}

Barcode Implementation

Code 128 Set C

Taxonomy IDs are optimized for Code 128 Set C encoding:
  • 18 digits = 9 symbol pairs
  • Highly compact barcode
  • Fast scanning

Label Format

┌─────────────────────────────────┐
│  [BARCODE: 011300000012345042]  │
│                                 │
│     ITEM #12345                 │
│     SKU: EXAMPLE-PRODUCT        │
└─────────────────────────────────┘

Validation

Client-Side Validation

function validateTaxonomyId(id: string): boolean {
  if (!/^\d{18}$/.test(id)) return false

  const base = id.substring(0, 16)
  const actualChecksum = id.substring(16, 18)

  // Use BigInt for accurate calculation with large numbers
  const expectedChecksum = (BigInt(base) % 97n).toString().padStart(2, '0')

  return actualChecksum === expectedChecksum
}

Server-Side Validation

The API validates taxonomy IDs using Zod schemas:
const taxonomyIdSchema = z.string().refine(
  (val) => /^\d{18}$/.test(val) && validateChecksum(val),
  { message: 'Invalid taxonomy ID' }
)

TypeScript Types

The taxonomy ID system exposes the following types and utilities from apps/api/src/lib/taxonomy-id.ts:

EntityType

type EntityType =
  | 'tote'
  | 'robot'
  | 'cart'
  | 'bag'
  | 'product'
  | 'sweep'
  | 'inventory_group'
  | 'staff'
  | 'retailer_location'
  | 'portal'
  | 'manifest'

TYPE_CODES

const TYPE_CODES: Record<EntityType, string> = {
  tote: '10',
  robot: '11',
  product: '13',
  sweep: '14',
  cart: '20',
  bag: '21',
  inventory_group: '22',
  staff: '23',
  retailer_location: '24',
  portal: '25',
  manifest: '27',
}

Utility Functions

FunctionDescription
generateTaxonomyId(type, seq, fac?, ver?, rsv?)Generate a taxonomy ID for a given entity type and sequence
validateTaxonomyId(id)Validate format and MOD-97 checksum
parseEntityType(id)Extract the EntityType from a taxonomy ID
parseTaxonomyId(id)Parse into all component parts (version, type, facility, sequence, reserved, checksum)
formatDisplayId(id)Extract the 5-digit display sequence
formatEntityLabel(id)Format as human-readable label (e.g., “Cart 00001”)
calculateChecksum(digits)Calculate MOD-97 checksum for a 16-digit base string

Best Practices

Use Taxonomy IDs for Display

Show taxonomy IDs in user interfaces and printed materials. They’re easier to read and verify than UUIDs.

Store UUIDs Internally

Use UUIDs for database foreign keys and internal references. Taxonomy IDs are for external interfaces.

Validate on Input

Always validate the checksum when accepting taxonomy ID input to catch typos early.

Include Both in Exports

When exporting data, include both the UUID and taxonomy ID for maximum compatibility.