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:
TYPEEntityInitial AllocationDescription
10ToteFAC=000, SEQ working bandContainer for order items
11RobotFAC=000, SEQ working bandDelivery robot
12Map LocationFAC=000, SEQ working bandWarehouse location node
13ItemFAC=000, SEQ working bandSellable product
14Job/TaskOptionalWork assignments
2049ReservedHoldNear-term entity classes
5089Vendor/BatchHoldLot tracking, experiments
9099Future SchemaDo not useReserved for schema evolution

Entity Type Details

Physical containers used to hold items during fulfillment.Use cases:
  • Order picking containers
  • Receiving containers
  • Inter-zone transfer bins
Example: 01 10 000 0000001 00 CS → Tote #1
Autonomous delivery robots for warehouse operations.Use cases:
  • Delivery robots
  • Picking assistants
  • Inventory scanners
Example: 01 11 000 0000027 00 CS → Robot #27
Physical locations within the warehouse grid.Use cases:
  • Aisles, bays, shelves, slots
  • Zone markers
  • Staging areas
Example: 01 12 000 0000042 00 CS → Map location #42
Sellable products in the catalog.Use cases:
  • Product lookup
  • Inventory tracking
  • Order line items
Example: 01 13 000 0012345 00 CS → Item #12345

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)

EntityFull ID (without CS)CalculationCSComplete Taxonomy ID
Tote #101 10 000 0000001 000110000000000100 mod 97XX011000000000010XX
Robot #2701 11 000 0000027 000111000000002700 mod 97XX011100000000270XX
Map loc #4201 12 000 0000042 000112000000004200 mod 97XX011200000000420XX
Item #1234501 13 000 0012345 000113000001234500 mod 97XX011300000123450XX
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

CREATE OR REPLACE FUNCTION generate_taxonomy_id()
RETURNS TRIGGER AS $$
DECLARE
  type_code TEXT;
  fac_code TEXT := '000';
  seq_num BIGINT;
  rsv_code TEXT := '00';
  base_id TEXT;
  checksum INT;
BEGIN
  -- Determine type code based on table
  CASE TG_TABLE_NAME
    WHEN 'totes' THEN type_code := '10';
    WHEN 'robots' THEN type_code := '11';
    WHEN 'inventory_groups' THEN type_code := '12';
    WHEN 'sellable_products' THEN type_code := '13';
    -- ... other tables
  END CASE;

  -- Get next sequence number
  seq_num := nextval('taxonomy_seq_' || type_code);

  -- Build base ID (first 16 digits)
  base_id := '01' || type_code || fac_code ||
             LPAD(seq_num::TEXT, 7, '0') || rsv_code;

  -- Calculate MOD-97 checksum
  checksum := base_id::NUMERIC % 97;

  -- Set complete taxonomy ID
  NEW.taxonomy_id := base_id || LPAD(checksum::TEXT, 2, '0');

  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

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:
// In route handlers
const id = await resolveId('sellable_products', idParam)
// Returns UUID regardless of input format

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.slice(0, 16)
  const checksum = parseInt(id.slice(16), 10)
  const calculated = parseInt(base) % 97

  return checksum === calculated
}

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' }
)

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.