This appendix documents how products and variants work in the POS Platform, with specific attention to Shopify compatibility and the Option1/Option2/Option3 system.
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ PRODUCT-VARIANT HIERARCHY │
└─────────────────────────────────────────────────────────────────────────────────────┘
PRODUCT (Parent)
┌──────────────────────────┐
│ id: 12345 │
│ name: "Galaxy V-Neck" │
│ description: "Soft..." │
│ vendor: "Acme Apparel" │
│ category: "Men's Tops" │
│ tags: ["summer", "new"] │
│ status: "active" │
│ │
│ ┌──────────────────────┐│
│ │ OPTIONS ││
│ │ 1. Color (Red, Blue) ││
│ │ 2. Size (S, M, L, XL)││
│ │ 3. (unused) ││
│ └──────────────────────┘│
└─────────────┬────────────┘
│
┌──────────────────────────┼──────────────────────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ VARIANT 1 │ │ VARIANT 2 │ │ VARIANT 3 │ │ VARIANT 8 │ ...
│ │ │ │ │ │ │ │
│ Red / S │ │ Red / M │ │ Red / L │ │ Blue / XL │
│ │ │ │ │ │ │ │
│ SKU: │ │ SKU: │ │ SKU: │ │ SKU: │
│ NXJ1078- │ │ NXJ1078- │ │ NXJ1078- │ │ NXJ1078- │
│ RED-S │ │ RED-M │ │ RED-L │ │ BLU-XL │
│ │ │ │ │ │ │ │
│ Barcode: │ │ Barcode: │ │ Barcode: │ │ Barcode: │
│ 0657381... │ │ 0657381... │ │ 0657381... │ │ 0657381... │
│ │ │ │ │ │ │ │
│ Price: │ │ Price: │ │ Price: │ │ Price: │
│ $29.00 │ │ $29.00 │ │ $29.00 │ │ $32.00 │
│ │ │ │ │ │ │ │
│ Inventory: │ │ Inventory: │ │ Inventory: │ │ Inventory: │
│ GM: 5 │ │ GM: 8 │ │ GM: 3 │ │ GM: 2 │
│ HM: 3 │ │ HM: 6 │ │ HM: 4 │ │ HM: 0 │
│ LM: 7 │ │ LM: 4 │ │ LM: 5 │ │ LM: 3 │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
Color × Size = 2 × 4 = 8 VARIANTS (Cartesian product)
| Data | Parent Product | Variant |
| Name/Title | ✅ “Galaxy V-Neck Tee” | Derived: “Galaxy V-Neck - Red / S” |
| Description | ✅ Full HTML description | ❌ (inherits from parent) |
| Category | ✅ “Men’s Tops” | ❌ (inherits from parent) |
| Vendor | ✅ “Acme Apparel” | ❌ (inherits from parent) |
| Tags | ✅ [“summer”, “new”, “sale”] | ❌ (inherits from parent) |
| SEO/URL | ✅ /products/galaxy-vneck | ❌ (one URL per product) |
| Options | ✅ Color, Size definitions | Values: Red, S |
| SKU | ❌ | ✅ Unique per variant |
| Barcode | ❌ | ✅ Unique UPC per variant |
| Price | Base price (optional) | ✅ Actual selling price |
| Cost | ❌ | ✅ Per-variant cost |
| Inventory | ❌ | ✅ Per-variant, per-location |
| Images | ✅ Multiple product images | Single variant-specific image |
Shopify supports exactly 3 options per product. This is a hard limit that cannot be extended via configuration or apps.
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ OPTION SYSTEM │
└─────────────────────────────────────────────────────────────────────────────────────┘
PRODUCT LEVEL:
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ options: [ │
│ { name: "Color", position: 1, values: ["Red", "Blue", "Green", "Black"] }, │
│ { name: "Size", position: 2, values: ["S", "M", "L", "XL", "XXL"] }, │
│ { name: "Material", position: 3, values: ["Cotton", "Polyester"] } │
│ ] │
└─────────────────────────────────────────────────────────────────────────────────────┘
VARIANT LEVEL:
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ { │
│ "sku": "SHIRT-RED-M-COT", │
│ "option1": "Red", ← Maps to options[0] (Color) │
│ "option2": "M", ← Maps to options[1] (Size) │
│ "option3": "Cotton", ← Maps to options[2] (Material) │
│ "price": 29.99, │
│ "barcode": "0657381512532" │
│ } │
└─────────────────────────────────────────────────────────────────────────────────────┘
| Industry | Option1 (Primary) | Option2 (Secondary) | Option3 (Tertiary) |
| Apparel | Color | Size | Material / Fit |
| Footwear | Size | Width | Color |
| Electronics | Capacity (32GB) | Color | Connectivity |
| Furniture | Color | Size | Material |
| Food | Flavor | Size | Pack Quantity |
VARIANT FORMULA: Option1.values × Option2.values × Option3.values
Example 1: Simple T-Shirt
┌─────────────────────────────────────────────────┐
│ Color: Red, Blue, Black (3 values) │
│ Size: S, M, L, XL (4 values) │
│ Material: (not used) │
│ │
│ Total Variants: 3 × 4 = 12 │
└─────────────────────────────────────────────────┘
Example 2: Premium Dress Shirt
┌─────────────────────────────────────────────────┐
│ Color: White, Blue, Pink, Lavender (4 values) │
│ Size: 14.5-32, 15-32, 15-34, ... (10 sizes) │
│ Fit: Slim, Regular (2 values) │
│ │
│ Total Variants: 4 × 10 × 2 = 80 │
└─────────────────────────────────────────────────┘
Example 3: Maximum Complexity (WARNING)
┌─────────────────────────────────────────────────┐
│ Color: 10 colors │
│ Size: 10 sizes │
│ Material: 3 materials │
│ │
│ Total Variants: 10 × 10 × 3 = 300 │
│ │
│ ⚠️ EXCEEDS SHOPIFY LIMIT OF 100! │
│ (2,048 limit available with new API) │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ PRODUCT CREATION WORKFLOW │
└─────────────────────────────────────────────────────────────────────────────────────┘
STEP 1: CREATE PARENT PRODUCT
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ Admin Portal → Products → [+ New Product] │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ BASIC INFORMATION │ │
│ │ │ │
│ │ Product Name * [Galaxy V-Neck Tee ] │ │
│ │ │ │
│ │ Description [────────────────────────────────────────────────] │ │
│ │ [Premium cotton v-neck with a modern fit. ] │ │
│ │ [Perfect for everyday wear. ] │ │
│ │ [────────────────────────────────────────────────] │ │
│ │ │ │
│ │ Vendor/Brand * [Nexus Premier ▼ ] │ │
│ │ Category * [Men's Tops > T-Shirts ▼ ] │ │
│ │ Product Type [Apparel ▼ ] │ │
│ │ │ │
│ │ Tags [summer] [new arrival] [basics] [+ Add Tag] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Save & Continue →] │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
STEP 2: DEFINE OPTIONS
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ PRODUCT OPTIONS │ │
│ │ │ │
│ │ Does this product have variants (size, color, etc.)? │ │
│ │ ● Yes, this product has multiple options │ │
│ │ ○ No, this is a simple product (single variant) │ │
│ │ │ │
│ │ ────────────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ OPTION 1 * │ │
│ │ Name: [Color ▼ ] │ │
│ │ Values: [Red ] [Blue ] [Navy ] [Black ] [+ Add] │ │
│ │ │ │
│ │ ────────────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ OPTION 2 [+ Add Option] │
│ │ Name: [Size ▼ ] │ │
│ │ Values: [S ] [M ] [L ] [XL ] [+ Add] │ │
│ │ │ │
│ │ ────────────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ OPTION 3 (Optional) [+ Add Option] │
│ │ Name: [ ▼ ] │ │
│ │ Values: │ │
│ │ │ │
│ │ ────────────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ VARIANT PREVIEW │ │
│ │ Based on your options, 16 variants will be created: │ │
│ │ │ │
│ │ Red/S, Red/M, Red/L, Red/XL, │ │
│ │ Blue/S, Blue/M, Blue/L, Blue/XL, │ │
│ │ Navy/S, Navy/M, Navy/L, Navy/XL, │ │
│ │ Black/S, Black/M, Black/L, Black/XL │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Save & Continue →] │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
STEP 3: CONFIGURE VARIANTS
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ VARIANT DETAILS [Bulk Edit] │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Default Price: [$29.00 ] Default Cost: [$12.00 ] │ │ │
│ │ │ [✓] Apply to all variants [✓] Apply to all variants │ │ │
│ │ └───────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Variant │ SKU │ Barcode │ Price │ Cost │ ✓ │ │ │
│ │ ├──────────────┼──────────────────┼───────────────┼────────┼────────┼────┤ │ │
│ │ │ Red / S │ NXJ1078-RED-S │ 0657381512501 │ $29.00 │ $12.00 │ ✓ │ │ │
│ │ │ Red / M │ NXJ1078-RED-M │ 0657381512502 │ $29.00 │ $12.00 │ ✓ │ │ │
│ │ │ Red / L │ NXJ1078-RED-L │ 0657381512503 │ $29.00 │ $12.00 │ ✓ │ │ │
│ │ │ Red / XL │ NXJ1078-RED-XL │ 0657381512504 │ $32.00 │ $13.00 │ ✓ │ │ │
│ │ │ Blue / S │ NXJ1078-BLU-S │ 0657381512505 │ $29.00 │ $12.00 │ ✓ │ │ │
│ │ │ Blue / M │ NXJ1078-BLU-M │ 0657381512506 │ $29.00 │ $12.00 │ ✓ │ │ │
│ │ │ ... │ ... │ ... │ ... │ ... │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ SKU PATTERN: [NXJ1078-{color:3}-{size}] [Auto-Generate SKUs] │ │
│ │ │ │
│ │ ⚠️ 2 variants have higher prices (XL sizes) │ │
│ │ ⚠️ 0 variants are missing barcodes │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Save & Continue →] │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
STEP 4: SET INVENTORY
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ INVENTORY BY LOCATION │ │
│ │ │ │
│ │ Track inventory: [✓] Track quantity [ ] Don't track (infinite stock) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Variant │ HQ (Warehouse) │ GM │ HM │ LM │ NM │ │ │
│ │ ├──────────────┼────────────────┼──────────┼──────────┼──────────┼────────┤ │ │
│ │ │ Red / S │ [100 ] │ [5 ] │ [3 ] │ [4 ] │ [2 ] │ │ │
│ │ │ Red / M │ [150 ] │ [8 ] │ [6 ] │ [7 ] │ [4 ] │ │ │
│ │ │ Red / L │ [120 ] │ [6 ] │ [5 ] │ [5 ] │ [3 ] │ │ │
│ │ │ Red / XL │ [80 ] │ [3 ] │ [2 ] │ [3 ] │ [2 ] │ │ │
│ │ │ Blue / S │ [90 ] │ [4 ] │ [3 ] │ [3 ] │ [2 ] │ │ │
│ │ │ ... │ ... │ ... │ ... │ ... │ ... │ │ │
│ │ └─────────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ TOTALS: │ │
│ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ HQ: 1,520 units │ GM: 92 │ HM: 68 │ LM: 75 │ NM: 45 │ │ │
│ │ │ │ │ │
│ │ │ NETWORK TOTAL: 1,800 units │ │ │
│ │ └─────────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Save & Continue →] │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
STEP 5: ADD IMAGES
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ PRODUCT IMAGES │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ [MAIN] │ │ [BACK] │ │ [RED] │ │ [BLUE] │ │ │
│ │ │ 📷 │ │ 📷 │ │ 📷 │ │ 📷 │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ Primary Position 2 Color swatch Color swatch │ │
│ │ → Red variants → Blue variants │ │
│ │ │ │
│ │ [+ Upload Images] [Link from URL] │ │
│ │ │ │
│ │ ────────────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ VARIANT IMAGE MAPPING (Optional) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Color │ Image │ │ │
│ │ ├──────────┼──────────────────────────────────────────────────────────────┤ │ │
│ │ │ Red │ [red-vneck.jpg ▼ ] │ │ │
│ │ │ Blue │ [blue-vneck.jpg ▼ ] │ │ │
│ │ │ Navy │ [navy-vneck.jpg ▼ ] │ │ │
│ │ │ Black │ [black-vneck.jpg ▼ ] │ │ │
│ │ └─────────────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [Save & Continue →] │
└─────────────────────────────────────────────────────────────────────────────────────┘
│
▼
STEP 6: REVIEW & PUBLISH
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ REVIEW PRODUCT │ │
│ │ │ │
│ │ ✓ Basic Information complete │ │
│ │ ✓ 2 options defined (Color, Size) │ │
│ │ ✓ 16 variants configured │ │
│ │ ✓ All variants have SKUs │ │
│ │ ⚠️ 4 variants missing barcodes │ │
│ │ ✓ Inventory set for all locations │ │
│ │ ✓ 4 images uploaded │ │
│ │ │ │
│ │ ────────────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ PUBLISH STATUS │ │
│ │ │ │
│ │ ○ Draft (not visible to customers) │ │
│ │ ● Active (visible on storefront) │ │
│ │ ○ Archived (hidden, preserved for records) │ │
│ │ │ │
│ │ ────────────────────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ SHOPIFY SYNC │ │
│ │ │ │
│ │ [✓] Sync to Shopify │ │
│ │ Product will be created/updated in Shopify store │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
│ │
│ [← Back to Edit] [Save as Draft] [Publish] │
│ │
└─────────────────────────────────────────────────────────────────────────────────────┘
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES shared.tenants(id),
-- Basic Information
name VARCHAR(255) NOT NULL,
description TEXT,
handle VARCHAR(255) NOT NULL, -- URL slug
vendor_id UUID REFERENCES vendors(id),
category_id UUID REFERENCES categories(id),
product_type VARCHAR(100),
-- Options Definition (up to 3)
option1_name VARCHAR(50), -- e.g., "Color"
option1_values TEXT[], -- e.g., {"Red", "Blue", "Black"}
option2_name VARCHAR(50), -- e.g., "Size"
option2_values TEXT[], -- e.g., {"S", "M", "L", "XL"}
option3_name VARCHAR(50), -- e.g., "Material"
option3_values TEXT[], -- e.g., {"Cotton", "Polyester"}
-- Status
status VARCHAR(20) DEFAULT 'draft', -- draft, active, archived
-- Shopify Sync
shopify_product_id BIGINT,
shopify_synced_at TIMESTAMPTZ,
-- Metadata
tags TEXT[],
metadata JSONB DEFAULT '{}',
-- Audit
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
created_by UUID,
deleted_at TIMESTAMPTZ, -- Soft delete
-- Constraints
UNIQUE (tenant_id, handle),
CHECK (status IN ('draft', 'active', 'archived'))
);
-- Indexes
CREATE INDEX idx_products_tenant_status ON products(tenant_id, status);
CREATE INDEX idx_products_category ON products(category_id);
CREATE INDEX idx_products_vendor ON products(vendor_id);
CREATE INDEX idx_products_shopify ON products(shopify_product_id);
CREATE INDEX idx_products_tags ON products USING GIN(tags);
CREATE TABLE product_variants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES shared.tenants(id),
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
-- Identifiers (CRITICAL - must be unique)
sku VARCHAR(100) NOT NULL,
barcode VARCHAR(50), -- UPC/EAN
-- Option Values (maps to parent's option definitions)
option1_value VARCHAR(100), -- e.g., "Red"
option2_value VARCHAR(100), -- e.g., "M"
option3_value VARCHAR(100), -- e.g., "Cotton"
-- Derived title (for display)
title VARCHAR(255) GENERATED ALWAYS AS (
COALESCE(option1_value, '') ||
CASE WHEN option2_value IS NOT NULL THEN ' / ' || option2_value ELSE '' END ||
CASE WHEN option3_value IS NOT NULL THEN ' / ' || option3_value ELSE '' END
) STORED,
-- Pricing
price DECIMAL(10,2) NOT NULL,
compare_at_price DECIMAL(10,2), -- "Was" price for sales
cost DECIMAL(10,2), -- Cost of goods
-- Physical
weight DECIMAL(8,2),
weight_unit VARCHAR(10) DEFAULT 'lb',
requires_shipping BOOLEAN DEFAULT true,
-- Tax
taxable BOOLEAN DEFAULT true,
tax_code VARCHAR(50),
-- Status
position INTEGER DEFAULT 1, -- Display order
is_active BOOLEAN DEFAULT true,
-- Shopify Sync
shopify_variant_id BIGINT,
shopify_inventory_item_id BIGINT,
shopify_synced_at TIMESTAMPTZ,
-- Audit
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
-- Constraints
UNIQUE (tenant_id, sku),
UNIQUE (tenant_id, barcode) WHERE barcode IS NOT NULL
);
-- Indexes
CREATE INDEX idx_variants_product ON product_variants(product_id);
CREATE INDEX idx_variants_sku ON product_variants(sku);
CREATE INDEX idx_variants_barcode ON product_variants(barcode);
CREATE INDEX idx_variants_shopify ON product_variants(shopify_variant_id);
CREATE TABLE inventory_levels (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES shared.tenants(id),
variant_id UUID NOT NULL REFERENCES product_variants(id) ON DELETE CASCADE,
location_id UUID NOT NULL REFERENCES locations(id),
-- Quantities
quantity_on_hand INTEGER NOT NULL DEFAULT 0,
quantity_committed INTEGER NOT NULL DEFAULT 0, -- Reserved for orders
quantity_available INTEGER GENERATED ALWAYS AS (quantity_on_hand - quantity_committed) STORED,
-- Reorder
reorder_point INTEGER,
reorder_quantity INTEGER,
-- Shopify Sync
shopify_inventory_level_id BIGINT,
-- Audit
updated_at TIMESTAMPTZ DEFAULT NOW(),
-- Constraints
UNIQUE (variant_id, location_id),
CHECK (quantity_on_hand >= 0),
CHECK (quantity_committed >= 0)
);
-- Indexes
CREATE INDEX idx_inventory_variant ON inventory_levels(variant_id);
CREATE INDEX idx_inventory_location ON inventory_levels(location_id);
CREATE INDEX idx_inventory_low_stock ON inventory_levels(quantity_available)
WHERE quantity_available <= reorder_point;
POST /api/v1/products
Content-Type: application/json
{
"name": "Galaxy V-Neck Tee",
"description": "Premium cotton v-neck with a modern fit.",
"vendorId": "vendor_abc123",
"categoryId": "cat_mens_tops",
"productType": "Apparel",
"tags": ["summer", "new arrival", "basics"],
"options": [
{ "name": "Color", "values": ["Red", "Blue", "Navy", "Black"] },
{ "name": "Size", "values": ["S", "M", "L", "XL"] }
],
"variants": [
{
"sku": "NXJ1078-RED-S",
"barcode": "0657381512501",
"option1Value": "Red",
"option2Value": "S",
"price": 29.00,
"cost": 12.00,
"inventory": [
{ "locationCode": "HQ", "quantity": 100 },
{ "locationCode": "GM", "quantity": 5 },
{ "locationCode": "HM", "quantity": 3 }
]
},
{
"sku": "NXJ1078-RED-M",
"barcode": "0657381512502",
"option1Value": "Red",
"option2Value": "M",
"price": 29.00,
"cost": 12.00,
"inventory": [
{ "locationCode": "HQ", "quantity": 150 },
{ "locationCode": "GM", "quantity": 8 }
]
}
// ... more variants
],
"status": "active",
"syncToShopify": true
}
{
"id": "prod_abc123",
"name": "Galaxy V-Neck Tee",
"handle": "galaxy-v-neck-tee",
"status": "active",
"shopifyProductId": 7891234567890,
"options": [
{ "name": "Color", "position": 1, "values": ["Red", "Blue", "Navy", "Black"] },
{ "name": "Size", "position": 2, "values": ["S", "M", "L", "XL"] }
],
"variants": [
{
"id": "var_001",
"sku": "NXJ1078-RED-S",
"title": "Red / S",
"price": 29.00,
"shopifyVariantId": 39332384801985,
"totalInventory": 108
},
{
"id": "var_002",
"sku": "NXJ1078-RED-M",
"title": "Red / M",
"price": 29.00,
"shopifyVariantId": 39332384802001,
"totalInventory": 158
}
],
"totalVariants": 16,
"totalInventory": 1800,
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-15T10:30:00Z"
}
{BRAND}{STYLE}-{OPTION1}-{OPTION2}[-{OPTION3}]
Examples:
NXJ1078-RED-S ← Nexus Clothing, Style 1078, Red, Small
NXP0892-KHK-32 ← Nexus Clothing, Pants 0892, Khaki, Size 32
BRT-BLU-155 ← Burton, Blue, 155cm
function generateSku(product, variant) {
const brand = product.vendorCode || 'GEN'; // 3-char brand code
const style = product.styleNumber; // 4-digit style
let sku = `${brand}${style}`;
if (variant.option1Value) {
sku += `-${abbreviate(variant.option1Value, 3)}`; // RED, BLU, BLK
}
if (variant.option2Value) {
sku += `-${variant.option2Value}`; // S, M, L, 32, 34
}
if (variant.option3Value) {
sku += `-${abbreviate(variant.option3Value, 3)}`; // COT, PLY
}
return sku.toUpperCase();
}
function abbreviate(value, length) {
// Standard abbreviations
const map = {
'Red': 'RED', 'Blue': 'BLU', 'Black': 'BLK', 'White': 'WHT',
'Navy': 'NAV', 'Green': 'GRN', 'Grey': 'GRY', 'Gray': 'GRY',
'Cotton': 'COT', 'Polyester': 'PLY', 'Wool': 'WOL',
'Small': 'S', 'Medium': 'M', 'Large': 'L', 'Extra Large': 'XL'
};
return map[value] || value.substring(0, length).toUpperCase();
}
| POS Field | Shopify Field | Notes |
name | title | Direct mapping |
description | body_html | May include HTML |
handle | handle | URL slug, auto-generated |
vendor.name | vendor | String, not ID |
category.name | product_type | String, not ID |
tags[] | tags | Comma-separated string |
status | status | active/draft/archived |
option1_name | options[0].name | e.g., “Color” |
option1_values[] | options[0].values[] | e.g., [“Red”, “Blue”] |
| POS Field | Shopify Field | Notes |
sku | sku | Primary sync key |
barcode | barcode | UPC/EAN |
option1_value | option1 | e.g., “Red” |
option2_value | option2 | e.g., “M” |
option3_value | option3 | e.g., “Cotton” |
price | price | Decimal as string |
compare_at_price | compare_at_price | Sale pricing |
cost | N/A | Shopify uses inventory_item.cost |
weight | weight | With weight_unit |
taxable | taxable | Boolean |
requires_shipping | requires_shipping | Boolean |
POS inventory_levels → Shopify InventoryLevel
─────────────────────────────────────────────────────────
variant.shopify_inventory_item_id → inventory_item_id
location.shopify_location_id → location_id
quantity_available → available
POST /admin/api/2025-01/inventory_levels/set.json
{
"location_id": 655441491,
"inventory_item_id": 808950810,
"available": 42
}
| Limit | Value | Workaround |
| Max Options | 3 | Split into separate products |
| Max Variants | 100 (legacy) / 2,048 (new) | Split into separate products |
| Max Images | 250 per product | Use CDN for extras |
| SKU Length | 255 chars | Keep under 100 recommended |
Split a product into multiple Shopify products when:
- More than 3 options needed (e.g., Size, Color, Material, Inseam)
- Variant count exceeds 100 (or 2,048 with new API)
- Variants need unique descriptions (Shopify shares description)
- SEO requires separate pages (different keywords per variant group)
SPLIT EXAMPLE:
─────────────────────────────────────────────────────
Original: "Dress Shirt" with Color × Size × Fit × Collar = 500+ variants
Split into:
├── "Dress Shirt - Slim Fit" (Color × Size = 100 variants)
├── "Dress Shirt - Regular Fit" (Color × Size = 100 variants)
├── "Dress Shirt - Classic Fit" (Color × Size = 100 variants)
└── ... (grouped by Fit, which becomes implicit)
Link them with:
- Same "product_group" tag
- Cross-reference in descriptions
- Collection grouping
Appendix H - Version 1.0
Product and Variant Model Documentation
Last updated: December 2025