Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/VisualGraphxLLC/API-HUB/llms.txt

Use this file to discover all available pages before exploring further.

API-HUB uses a polymorphic catalog model. Every product carries a product_type field that determines which detail table is populated, which pricing resolver is invoked, and which UI components render in the frontend. The two supported types are apparel and print.

Shared product structure

Both types share the same products table and a set of common child tables. The fields below apply regardless of product_type.
FieldTypeNotes
idUUIDInternal primary key
supplier_idUUIDFK → suppliers.id
supplier_skuVARCHAR(255)Unique per supplier
product_nameVARCHAR(500)
brandVARCHAR(255)Optional
product_typeVARCHAR(50)"apparel" or "print"
last_syncedtimestamptzSet on each successful sync; drives stale detection
archived_attimestamptzNon-null = soft-deleted; excluded from default queries

Common child tables

Stores each color/size combination for a product. For apparel, this is the primary pricing anchor. For print, variants are rarely used since pricing is formula-based.
ColumnTypeNotes
idUUID
product_idUUIDFK → products.id
colorVARCHAR(100)Nullable
sizeVARCHAR(50)Nullable
skuVARCHAR(255)Supplier’s part-level SKU
base_priceNUMERIC(10,2)Fallback if no tiered prices exist
inventoryINTEGERNullable
warehouseVARCHAR(255)Nullable
The unique constraint is (product_id, color, size).
Images are stored per product and can be associated with a specific color swatch. The images_by_color field in the ProductRead schema groups them automatically by normalizing the color value to a lowercase underscore key (e.g., "Royal Blue""royal_blue").
ColumnTypeNotes
urlTEXTCDN or proxied URL
supplier_image_urlTEXTOriginal supplier URL
image_typeVARCHAR(50)front, back, side, detail, lifestyle
colorVARCHAR(100)Nullable; links image to a color variant
sort_orderINTEGERAscending sort applied before grouping
checksumVARCHAR(64)SHA-256; used for change detection
Product customization options (e.g., imprint method, thread color) are stored in product_options. Each option has one or more product_option_attributes. Options can carry per-attribute pricing (price, setup_cost, multiplier).Options and attributes carry OPS IDs (ops_option_id, ops_attribute_id) and master IDs (master_option_id, master_attribute_id) for mapping to OnPrintShop entities during push.The enabled flag controls whether an option appears in the OPS push payload. Options default to enabled = false — you must explicitly enable them in the UI before pushing.
For print products, suppliers often provide a list of supported sizes (e.g., 4×6, 5×7, 8×10). These are stored in product_sizes with width, height, unit (default "in"), and an optional label.Sizes are separate from the bounds in print_details — bounds define the continuous valid range; sizes define common presets.

Apparel products

Apparel products (product_type = "apparel") represent garments and similar merchandise sold by color and size. Pricing is resolved by matching the requested quantity to a tier band in the variant_prices table.

Apparel-specific table: apparel_details

This table has a 1-to-1 relationship with products (keyed on product_id).
ColumnTypeDefaultNotes
pricing_methodVARCHAR(50)"tiered_variant"Always tiered_variant for apparel
raw_payloadJSONBnullFull supplier API response; stored for debugging
The raw_payload column stores the unmodified supplier API response and is never exposed in the public API. It is available to adapters and debugging tools only.

Tiered pricing: variant_prices

Each variant can have multiple price rows, one per combination of price_type and quantity_min. The TieredVariantResolver selects the best matching row for the requested quantity using the priority Net > Sale > MSRP > Case.
ColumnTypeNotes
variant_idUUIDFK → product_variants.id
price_typeVARCHAR(20)MSRP, Net, Sale, or Case
quantity_minINTEGERLower bound of the tier (inclusive)
quantity_maxINTEGERUpper bound of the tier; null = no upper limit
priceNUMERIC(10,2)Unit price at this tier
The unique constraint is (variant_id, price_type, quantity_min). A variant with no variant_prices rows falls back to base_price on the variant row. If both are absent, the resolver raises MissingPricingDataError.

Example: apparel product response

{
  "id": "a1b2c3d4-0000-0000-0000-000000000001",
  "supplier_sku": "PC61",
  "product_name": "Port & Company Essential Tee",
  "product_type": "apparel",
  "brand": "Port & Company",
  "last_synced": "2026-05-07T10:00:00Z",
  "variants": [
    {
      "id": "v1000000-0000-0000-0000-000000000001",
      "color": "Athletic Heather",
      "size": "S",
      "sku": "PC61-ATH-S",
      "base_price": 4.98,
      "inventory": 2400,
      "prices": [
        { "price_type": "Net",  "quantity_min": 1,   "quantity_max": 11,   "price": "6.98" },
        { "price_type": "Net",  "quantity_min": 12,  "quantity_max": 71,   "price": "5.98" },
        { "price_type": "Net",  "quantity_min": 72,  "quantity_max": null, "price": "4.98" },
        { "price_type": "MSRP", "quantity_min": 1,   "quantity_max": null, "price": "12.99" }
      ]
    }
  ],
  "apparel_details": {
    "pricing_method": "tiered_variant",
    "raw_payload": null
  }
}

Frontend: ApparelDetailPanel

The ApparelDetailPanel component renders the color × size matrix. Selecting a color filters the image gallery to show only images matching that color key. The tier table is displayed below the variant selector.
Print products (product_type = "print") represent flat printed goods — banners, labels, business cards, and similar items — whose price depends on the physical dimensions of the print area. This table has a 1-to-1 relationship with products (keyed on product_id).
ColumnTypeDefaultNotes
pricing_methodVARCHAR(50)"formula"Always formula for print
min_widthNUMERIC(10,2)nullMinimum allowed width
max_widthNUMERIC(10,2)nullMaximum allowed width
min_heightNUMERIC(10,2)nullMinimum allowed height
max_heightNUMERIC(10,2)nullMaximum allowed height
size_unitVARCHAR(10)"in"Unit for all dimension fields
base_price_per_sq_unitNUMERIC(10,4)nullPrimary pricing coefficient
raw_payloadJSONBnullMay contain a formula dict (see below)

Formula pricing

The FormulaResolver computes price as:
unit_price = base × width × height × area_factor
total      = unit_price × qty + setup_cost
The formula parameters are sourced in priority order:
  1. raw_payload.formula dict: { "base": "1.50", "area_factor": "0.04", "base_setup": "0.00" }
  2. base_price_per_sq_unit column with area_factor = 1 and base_setup = 0
If neither is present, the resolver raises MissingPricingDataError. Dimension bounds are enforced before the formula runs. If width or height fall outside the [min_*, max_*] range, a BoundsError is raised and the request returns 422.

Example: print product response

{
  "id": "b2c3d4e5-0000-0000-0000-000000000002",
  "supplier_sku": "BNR-36X96",
  "product_name": "Vinyl Banner",
  "product_type": "print",
  "last_synced": "2026-05-07T08:30:00Z",
  "variants": [],
  "print_details": {
    "pricing_method": "formula",
    "min_width": "12.00",
    "max_width": "144.00",
    "min_height": "12.00",
    "max_height": "96.00",
    "size_unit": "in",
    "base_price_per_sq_unit": "0.0095",
    "raw_payload": {
      "formula": {
        "base": "0.0095",
        "area_factor": "1.0",
        "base_setup": "25.00"
      }
    }
  },
  "sizes": [
    { "width": "24.00", "height": "36.00", "unit": "in", "label": "24×36" },
    { "width": "36.00", "height": "96.00", "unit": "in", "label": "36×96" }
  ]
}

Frontend: DimensionInput and PrintDetailPanel

The DimensionInput component reads min_width, max_width, min_height, and max_height from the product and applies HTML min/max constraints on its number inputs. Dimensions outside the allowed range are rejected before a quote request is sent. The PrintDetailPanel shows dimension bounds, preset sizes from product_sizes, and a live price preview powered by the debounced quote hook.

Ingest validation

When a supplier adapter submits a product, the ProductIngest schema enforces type-specific rules:
  • product_type = "apparel" — an apparel_details block is auto-created with pricing_method = "tiered_variant" if omitted.
  • product_type = "print" — either print_details or at least one entry in sizes must be provided; the validator raises a ValueError otherwise.
@model_validator(mode="after")
def validate_type_details(self) -> "ProductIngest":
    if self.product_type == "apparel" and not self.apparel_details:
        self.apparel_details = ApparelDetailsIngest()
    if self.product_type == "print" and not self.print_details and not self.sizes:
        raise ValueError("print_details or sizes must be provided for product_type='print'")
    return self

Comparing product types

Apparel

  • product_type = "apparel"
  • Pricing via TieredVariantResolver
  • Requires variant_id in quote requests
  • Color × size matrix in product_variants
  • Tiered bands in variant_prices
  • Detail table: apparel_details

Print

  • product_type = "print"
  • Pricing via FormulaResolver
  • Requires width + height in quote requests
  • Dimension bounds enforced at quote time
  • Optional preset sizes in product_sizes
  • Detail table: print_details

Build docs developers (and LLMs) love