Markup rules are the pricing layer that sits between a supplier’s wholesale cost and the price displayed on an OnPrintShop storefront. For every product a customer pushes, API-HUB evaluates the customer’s rules, selects the best match, and applies the configured percentage, margin floor, and rounding strategy. The resulting prices are embedded in the push payload that n8n sends to OPS.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.
How rule resolution works
The markup engine (backend/modules/markup/engine.py) evaluates all rules for a customer in a single pass and returns the one rule that most specifically matches the product being priced. Specificity is ordered from most to least:
- Product-level —
scope = "product:{supplier_sku}". Matches exactly one SKU family. - Category-level —
scope = "category:{category}". Matches all products in a category (e.g.,"category:T-Shirts"). - Global —
scope = "all". Customer-wide catch-all.
priority value wins. Priority is an integer — there is no upper bound. This lets you layer exceptions cleanly: set the global rule to priority 0, category overrides to 10, and product-specific rules to 100.
If no rule matches a product (the customer has no rules at all, or none cover the product’s SKU or category), the engine returns the supplier’s base price unchanged — no markup is applied.
Markup rule fields
scope
scope
Controls which products the rule applies to. Three formats are accepted:
"all"— applies to every product for this customer."category:T-Shirts"— applies to all products whosecategoryfield matchesT-Shirtsexactly."product:PC61"— applies to the product whosesupplier_skuisPC61(Port & Company Essential Tee, for example).
markup_pct
markup_pct
The percentage to add on top of the supplier base price. Stored as a
NUMERIC(5,2) — for example, 45.00 means a 45% markup. The calculation is base_price × (1 + markup_pct / 100).min_margin
min_margin
An optional minimum margin percentage. If the computed price after
markup_pct falls below base_price × (1 + min_margin / 100), the price is raised to the floor. This protects against closeout or sale prices eroding your margin.rounding
rounding
Post-markup rounding strategy. Three values:
"none"— round to the nearest cent only."nearest_99"— round down to the nearest whole dollar, then add$0.99(e.g.,$14.23 → $13.99)."nearest_dollar"— round to the nearest whole dollar (standard banker’s rounding).
priority
priority
Integer tiebreaker within a scope level. Higher wins. Defaults to
0. Use higher values for exceptions that should override a base rule at the same scope.Creating and managing rules
Rules are scoped to a single customer. There is no limit on how many rules you can create for a customer.- List rules
- Create a rule
- Delete a rule
priority DESC. No authentication header is required for reads (protected at the network level in production).Price calculation in detail
The engine applies rules in this exact sequence for every variant in the product:Decimal type with str() coercion on float inputs to avoid floating-point drift. The base_price used in markup is the supplier’s wholesale cost stored per variant in product_variants.base_price.
Fetch the computed push payload
After rules are configured, you can inspect exactly what n8n will receive before triggering a push. This endpoint is the same one n8n calls internally.final_price on each variant is what gets written to OPS via setProductPrice. The markup_rule block tells you which rule was applied — useful for auditing unexpected prices.
OPS-ready variant bundles and options
Two additional endpoints pre-shape data into the structures OPS mutations expect directly.Variants bundle
GET /api/push/{customer_id}/product/{product_id}/ops-variants?ops_products_id=0Returns parallel sizes and prices arrays aligned by index, ready to loop over in n8n for setProductSize and setProductPrice mutations. Pass the OPS products_id returned by setProduct as ops_products_id.Options
GET /api/push/{customer_id}/product/{product_id}/ops-optionsReturns enabled product options shaped for OPS setAdditionalOption mutations. Includes source_master_option_id and per-attribute source_master_attribute_id for push mapping traceback. Both endpoints require X-Ingest-Secret.Storefront-level pricing overrides
For finer control beyond customer-wide markup rules,product_storefront_configs.pricing_overrides accepts per-product exceptions stored as a JSONB object. These are applied by the push pipeline on top of (or instead of) the markup rule result.
| Override key | Behavior |
|---|---|
fixed_unit_price | Bypasses markup calculation entirely. The value is used as-is for all variants. |
extra_markup_pct | Added on top of the markup rule result. Useful for individual products that carry higher fulfillment costs. |
nearest_99 | true / false. Overrides the rule’s rounding strategy with nearest_99 for this product. |
nearest_dollar | true / false. Overrides the rule’s rounding strategy with nearest_dollar for this product. |