Pricing tier fields & guards reference
This page is a lookup for every field on a pricing tier, the rules that constrain relative tiers (chain depth, circular references, currency inheritance), and the guards that block a tier from being deleted while it's in use.
For the task of creating and editing tiers, see Manage pricing tiers. For the mental model behind relative tiers and how their prices are derived, see Relative pricing tiers. To apply a tier's price to a specific product, see Set a product's price.
Before you begin
Pricing tiers are an account-level catalog setting under Products → Pricing Tiers. Managing them requires the pricing-tier settings permission; see Product permissions reference.
Field reference
Every field below appears on the pricing-tier API resource. Numeric adjustment fields are stored and returned with 4 decimal places (decimal:4).
| Field | Type | Applies to | Notes |
|---|---|---|---|
id | integer | all | Read-only identifier. |
name | string, max 255 | all | Required and unique across pricing tiers. Also used as the import-mapping key (price.<name>.value), so renaming affects integration mappings. |
pricing_type | enum absolute | relative | all | Defaults to absolute when omitted on create. An absolute tier holds a price you set per product; a relative tier derives its price from a source. |
relative_basis | enum pricing_tier | supplier_pricing_tier | attribute | relative | The source a relative tier derives from. null for absolute tiers. Required when pricing_type is relative. |
base_pricing_tier_id | integer, nullable | relative (pricing_tier basis) | The tier this one derives from. Required when relative_basis is pricing_tier. |
base_pricing_tier_name | string | relative | Read-only; included when the base tier is loaded. |
base_supplier_pricing_tier_id | integer, nullable | relative (supplier_pricing_tier basis) | The supplier pricing tier this one reads cost from (cost-plus). |
base_supplier_pricing_tier_name | string | relative | Read-only; included when loaded. |
base_attribute_id | integer, nullable | relative (attribute basis) | The numeric/integer product attribute this tier derives from. Required when relative_basis is attribute. The attribute must be a numeric or integer type. |
base_attribute_name | string | relative | Read-only; included when loaded. |
attr_rate | decimal(4), -999999.9999–999999.9999 | relative (attribute basis) | Multiplier in the attribute formula. Required for the attribute basis. |
attr_offset | decimal(4), same range | relative (attribute basis) | Additive offset in the attribute formula. |
adjustment_percentage | decimal(4), -99.9999–999.9999 | relative (pricing_tier, supplier_pricing_tier) | Percentage adjustment applied to the base price. Required for the pricing-tier and supplier bases. |
adjustment_fixed_amount | decimal(4), -999999.9999–999999.9999 | relative | Flat amount added after rounding. |
rounding_value | decimal(4), > 0, max 999999.9999 | relative | Optional. When set, the price is rounded to the nearest multiple of this value; when empty, the price is rounded to 2 decimals. |
currency_code | string, must exist in currencies | all | Set on create only; not editable on update for any tier. For pricing-tier and supplier basis relative tiers it's inherited from the source on save (see below); an attribute-basis tier keeps the currency you set on create. |
is_default | boolean | all | Marks the account's default pricing tier. Setting a tier default clears the flag on all others. The default tier can't be relative, can't be archived, and can't be deleted. |
chain_depth | integer (computed) | all | 0 for absolute tiers; 1–3 for relative tiers, counting the hops back to the root absolute tier. |
dependent_tier_count | integer (computed) | all | How many tiers use this tier as their base; included when counted. |
created_at / updated_at | datetime | all | Read-only timestamps. |
archived_at | datetime, nullable | all | Set when the tier is archived; archived tiers are excluded from lists by default. |
How relative prices are calculated
The two relative formulas share a rounding-then-fixed-amount tail, and every result is floored at 0:
- Pricing-tier / supplier basis:
intermediate = basePrice × (1 + adjustment_percentage ÷ 100), then apply rounding, then addadjustment_fixed_amount. - Attribute basis:
intermediate = attributeValue × attr_rate + attr_offset, then apply rounding, then addadjustment_fixed_amount. - Rounding step: when
rounding_valueis set and greater than 0,round(intermediate ÷ rounding_value) × rounding_value; otherwiseround(intermediate, 2).
Relative chain depth & circular references
Relative tiers can point at other relative tiers, forming a chain that ends at a root absolute tier. Two rules keep those chains valid, enforced at validation time on create and update. Both checks apply only to the pricing_tier basis — supplier and attribute bases read from a fixed source and can't form a tier-to-tier cycle.
| Rule | Behavior | Error |
|---|---|---|
| Self-reference | A tier can't set itself as its own base. | A pricing tier cannot reference itself as a base tier. |
| Circular reference | Setting a base whose own chain leads back to this tier is rejected. The check walks up from the proposed base, tracking every tier visited. | Cannot set this base tier: it would create a circular reference. |
| Maximum chain depth | The chain from a tier back to its root absolute tier may be at most 3 hops deep; a deeper chain is rejected. | Maximum chain depth of 3 exceeded. |
| Default can't be relative | The default tier can't be changed to relative. | The default pricing tier cannot be set to relative. |
A validation failure returns HTTP 422 with the message on the relevant field (base_pricing_tier_id for self/cycle/depth, pricing_type for the default-relative case).
The chain-walking helpers (resolveChain, getRootTier) also carry runtime loop-safety guards independent of validation: chain resolution stops after 10 hops, and root resolution stops if it revisits the starting tier. These guards protect the price calculation against a malformed chain that somehow bypassed validation, so a computed chain_depth never runs away.
Currency inheritance
currency_code is required when you create a tier, but a relative tier doesn't keep its own currency independent of its source — the currency is resolved automatically on save:
| Tier | Resulting currency |
|---|---|
| Absolute tier | The currency_code you set on create. |
Relative, pricing_tier basis | Inherited from the root absolute tier of its chain (getRootTier()->currency_code). |
Relative, supplier_pricing_tier basis | Inherited from the base supplier pricing tier's currency (falls back to the tier's own currency if the supplier tier has none). |
Relative, attribute basis | Not inherited — keeps the currency_code you set on create. |
| Any tier with an empty currency after the above | Falls back to the account's default currency (Currency::default()->code). |
currency_code is dropped from the update rules for every tier — you set it once on create. On each save, pricing_tier and supplier basis relative tiers re-resolve their currency from the source; absolute and attribute-basis tiers keep the value you set.
Delete guards
Deleting a pricing tier first checks whether it's in use. If any guard matches, the delete is blocked, the tier is left intact, and the API returns HTTP 400 with one error per matching reason (each keyed with a resource-linked code). If nothing matches, the tier's product pivots are detached and the tier is deleted.
| Guard | Blocked when | Reason returned |
|---|---|---|
| Default tier | is_default is true | You can't delete the default product pricing tier '<name>' |
| In use by products | Any product has a price stored against this tier (product_prices_count > 0) | Count of products currently using the tier. |
| Integration import mappings | Any data-import mapping references it via mapping->sku_field = price.<name>.value | Count of integration listing mappings using the tier. |
| Base for active tiers | Any non-archived tier lists this one as its base (active dependent tiers) | This tier is used as a base for: <names> |
To delete a blocked tier, first clear what depends on it — remove the tier's price from the products using it (see Set a product's price), remove or repoint any dependent relative tiers, and remove any integration import mappings that target it. A default tier can never be deleted; assign the default flag to another tier first.
Archive guards (related)
Archiving is a softer alternative but carries its own two guards, both returning HTTP 400: a tier that's the default can't be archived, and a tier that's a base for active dependent tiers can't be archived. Unlike delete, archiving doesn't check product usage or import mappings — an archived tier is simply hidden from lists by default while its historical prices remain. See Archive, unarchive & delete a product for the same pattern applied to products.
Next steps
- Manage pricing tiers — create, edit, archive and delete tiers.
- Relative pricing tiers — the concept behind bases, chains and derived prices.
- Set a product's price — apply a tier's price to a product.
- Product permissions reference — who can manage pricing tiers.