The pricing engine is a strategy-based system. When a quote request arrives,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.
resolve_quote loads the product, reads product_type, instantiates the appropriate resolver, and returns a QuoteResult with a full cost breakdown. No markup is applied at this layer — the public quote endpoint exposes raw supplier cost, making it safe to call from frontend components without leaking business pricing rules.
Two resolvers are implemented: TieredVariantResolver for apparel and FormulaResolver for print.
TieredVariantResolver (apparel)
Apparel pricing is determined by matching the requested quantity to a tier band in thevariant_prices table. A variant_id is required — prices are stored per variant (color + size combination), not per product.
Tier selection logic
The resolver queries allvariant_prices rows for the variant where:
Net and MSRP prices for the same band — the resolver applies this priority:
| Priority | price_type |
|---|---|
| 1 (highest) | Net |
| 2 | Sale |
| 3 | MSRP |
| 4 (lowest) | Case |
variant.base_price. If base_price is also absent, MissingPricingDataError is raised.
Apparel quote request
Apparel quote response
fallback: true in the breakdown means no tier row matched and base_price was used instead.
FormulaResolver (print)
Print pricing is area-based. The resolver reads dimension bounds and formula parameters fromprint_details, validates the requested width and height, then computes:
width and height are required in the request body. Omitting either raises BoundsError (422).
Formula parameter source
Parameters are resolved in priority order:print_details.raw_payload.formula— an explicit formula dict stored during adapter hydration:print_details.base_price_per_sq_unit— a single coefficient witharea_factor = 1andbase_setup = 0implied.
MissingPricingDataError is raised.
Bounds enforcement
Before the formula runs,_check_bounds validates the requested dimensions against the product’s min/max columns:
| Check | Error message |
|---|---|
width < min_width | "width {w} below minimum {min}" |
width > max_width | "width {w} above maximum {max}" |
height < min_height | "height {h} below minimum {min}" |
height > max_height | "height {h} above maximum {max}" |
Print quote request
Print quote response
area = width × height (in square inches when size_unit = "in"). total includes setup_cost added once per job, not per unit.
Public quote endpoint
POST /api/pricing/quote
No authentication required. Returns base cost with no markup applied. Safe to call from storefronts and frontend components. Request body fields| Field | Type | Required | Notes |
|---|---|---|---|
product_id | UUID | Yes | |
variant_id | UUID | Apparel only | Required for product_type = "apparel" |
width | Decimal (≥ 0) | Print only | Required for product_type = "print" |
height | Decimal (≥ 0) | Print only | Required for product_type = "print" |
qty | Integer (> 0) | Yes | |
selected_attribute_ids | UUID[] | No | Reserved for option multiplier support |
extra = "forbid" on the Pydantic model).
Response: QuoteResult
| Field | Type | Notes |
|---|---|---|
unit_price | Decimal | Rounded to 2 decimal places (ROUND_HALF_UP) |
total | Decimal | unit_price × qty for apparel; unit_price × qty + setup_cost for print |
currency | string | Always "USD" |
breakdown | ApparelBreakdown or PrintBreakdown | See resolver sections above |
Customer quote endpoint (internal only)
POST /api/customers//pricing/quote
Returns the base price with customer-specific markup rules and storefront overrides applied. Only n8n workflows should call this endpoint. RequestCustomerQuoteResult
Extends QuoteResult with markup metadata.
| Field | Type | Notes |
|---|---|---|
unit_price | Decimal | Post-markup unit price |
total | Decimal | Post-markup total |
base_unit_price | Decimal | Raw supplier cost before markup |
markup_pct | Decimal | Markup percentage applied, if any |
rounding | string | Rounding strategy applied (e.g. "nearest_cent") |
storefront_override_applied | bool | True if a per-storefront price override was used |
Live pricing in the frontend
The frontend uses a 250ms debounced hook (use-debounced-quote.ts) that fires POST /api/pricing/quote whenever the user changes quantity or print dimensions. This prevents flooding the API during rapid input.
- Apparel flow
- Print flow
- User selects a color and size → a
variant_idbecomes available. - User changes the quantity field.
- After 250ms of inactivity, the hook sends a quote request with
variant_idandqty. - The response updates the displayed unit price and tier match badge in real time.
Error reference
BoundsError — 422 Unprocessable Entity
BoundsError — 422 Unprocessable Entity
Raised when a print product’s
width or height falls outside the min_* / max_* bounds declared in print_details, or when width or height are omitted entirely for a print product.MissingPricingDataError — 404 or 422
MissingPricingDataError — 404 or 422
Raised when required pricing data is absent. HTTP status depends on what is missing:
- 404 — the
product_iditself does not exist in the database. - 422 — the product exists but is missing a
variant_id(apparel), has novariant_pricesand nobase_price, or has noprint_details/ formula (print).
Rounding
All monetary values are rounded usingROUND_HALF_UP (not banker’s rounding) to 2 decimal places via the shared to_cents helper:
unit_price, total, base, setup_cost, and all breakdown fields. The area field in PrintBreakdown is not rounded — it preserves full precision as an intermediate value.