API-HUB uses a polymorphic catalog model. Every product carries aDocumentation 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.
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 sameproducts table and a set of common child tables. The fields below apply regardless of product_type.
| Field | Type | Notes |
|---|---|---|
id | UUID | Internal primary key |
supplier_id | UUID | FK → suppliers.id |
supplier_sku | VARCHAR(255) | Unique per supplier |
product_name | VARCHAR(500) | |
brand | VARCHAR(255) | Optional |
product_type | VARCHAR(50) | "apparel" or "print" |
last_synced | timestamptz | Set on each successful sync; drives stale detection |
archived_at | timestamptz | Non-null = soft-deleted; excluded from default queries |
Common child tables
product_variants — color × size matrix
product_variants — color × size matrix
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.
The unique constraint is
| Column | Type | Notes |
|---|---|---|
id | UUID | |
product_id | UUID | FK → products.id |
color | VARCHAR(100) | Nullable |
size | VARCHAR(50) | Nullable |
sku | VARCHAR(255) | Supplier’s part-level SKU |
base_price | NUMERIC(10,2) | Fallback if no tiered prices exist |
inventory | INTEGER | Nullable |
warehouse | VARCHAR(255) | Nullable |
(product_id, color, size).product_images — multi-angle, color-keyed
product_images — multi-angle, color-keyed
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").| Column | Type | Notes |
|---|---|---|
url | TEXT | CDN or proxied URL |
supplier_image_url | TEXT | Original supplier URL |
image_type | VARCHAR(50) | front, back, side, detail, lifestyle |
color | VARCHAR(100) | Nullable; links image to a color variant |
sort_order | INTEGER | Ascending sort applied before grouping |
checksum | VARCHAR(64) | SHA-256; used for change detection |
product_options and product_option_attributes
product_options and product_option_attributes
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.product_sizes — discrete preset dimensions
product_sizes — discrete preset dimensions
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).
| Column | Type | Default | Notes |
|---|---|---|---|
pricing_method | VARCHAR(50) | "tiered_variant" | Always tiered_variant for apparel |
raw_payload | JSONB | null | Full 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.
| Column | Type | Notes |
|---|---|---|
variant_id | UUID | FK → product_variants.id |
price_type | VARCHAR(20) | MSRP, Net, Sale, or Case |
quantity_min | INTEGER | Lower bound of the tier (inclusive) |
quantity_max | INTEGER | Upper bound of the tier; null = no upper limit |
price | NUMERIC(10,2) | Unit price at this tier |
(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
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
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.
Print-specific table: print_details
This table has a 1-to-1 relationship with products (keyed on product_id).
| Column | Type | Default | Notes |
|---|---|---|---|
pricing_method | VARCHAR(50) | "formula" | Always formula for print |
min_width | NUMERIC(10,2) | null | Minimum allowed width |
max_width | NUMERIC(10,2) | null | Maximum allowed width |
min_height | NUMERIC(10,2) | null | Minimum allowed height |
max_height | NUMERIC(10,2) | null | Maximum allowed height |
size_unit | VARCHAR(10) | "in" | Unit for all dimension fields |
base_price_per_sq_unit | NUMERIC(10,4) | null | Primary pricing coefficient |
raw_payload | JSONB | null | May contain a formula dict (see below) |
Formula pricing
TheFormulaResolver computes price as:
raw_payload.formuladict:{ "base": "1.50", "area_factor": "0.04", "base_setup": "0.00" }base_price_per_sq_unitcolumn witharea_factor = 1andbase_setup = 0
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
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, theProductIngest schema enforces type-specific rules:
product_type = "apparel"— anapparel_detailsblock is auto-created withpricing_method = "tiered_variant"if omitted.product_type = "print"— eitherprint_detailsor at least one entry insizesmust be provided; the validator raises aValueErrorotherwise.
Comparing product types
Apparel
product_type = "apparel"- Pricing via
TieredVariantResolver - Requires
variant_idin quote requests - Color × size matrix in
product_variants - Tiered bands in
variant_prices - Detail table:
apparel_details
product_type = "print"- Pricing via
FormulaResolver - Requires
width+heightin quote requests - Dimension bounds enforced at quote time
- Optional preset sizes in
product_sizes - Detail table:
print_details