borkoldocs

Configurator

How the configurator works

Concepts behind configurable products, the schema, and the pricing model.

View as Markdown

Configurable products let a merchant sell items that are priced by dimensions and options instead of a flat price: verandas priced by width and depth, cut-to-size panels, made-to-order furniture with material and accessory choices. The storefront flow is:

  1. Fetch the product with GET /v1/api/commerce/products/{id}. A configurable product has "type": "configurable".
  2. Fetch its configuration schema with GET /v1/api/commerce/configurator/products/{id}/schema and render a configurator UI from it.
  3. On every change to the buyer's selection, post the current selection to POST /v1/api/commerce/configurator/products/{id}/quote to get the live price with a line-item breakdown.

Both configurator endpoints use the same authentication as the catalog endpoints: a tenant API key with the commerce:read scope as Authorization: Bearer {api_key}. The quote endpoint is a pure calculation; it creates no cart or order and therefore also requires only commerce:read.

Building blocks

A configurable product is described by four kinds of entities. All IDs are UUIDs.

Dimensions

A dimension is a numeric input the buyer must provide, for example width and depth. Each dimension has a key (the request key used in quote calls), a display label, a display unit string (for example "cm"), and an integer min and max. Dimensions are ordered by position; that order determines how dimension values map to price matrix axes (the first dimension is dim1, the second is dim2).

A dimension can carry constraints: per-choice tightenings of its allowed range. When the buyer selects a choice that has a constraint on a dimension, the dimension's effective min is raised to the constraint's min (if higher) and its effective max is lowered to the constraint's max (if lower). Constraints only ever tighten the range, never widen it.

Option groups and choices

Options are organized in groups. A group has a type of single (the buyer may pick at most one choice from the group) or multi (any number), and a required flag (at least one choice from the group must be selected). Groups and choices are both ordered by position.

Each choice can affect the price through a delta:

delta_typeMeaning of delta_value
fixedAmount in minor units (cents), added once.
per_areaRate in minor units per square meter. The delta is delta_value times the area, where area is dim1 * dim2 / 10000 square meters (dimensions are entered in cm). Requires the product to have exactly two dimensions; otherwise the quote fails with a 422.
percentPercentage of the base price, stored times 100: a delta_value of 1050 means 10.50 percent. The delta is base * delta_value / 10000.
percent_of_totalSame encoding, but applied to the subtotal after all other deltas (see the pricing walkthrough below).

Choices also carry is_default (a UI hint for the initial selection), an images array, and a requires_quote flag. When requires_quote is true, the storefront should route the buyer to a manual quote request instead of checkout; the quote endpoint still prices the configuration, the flag is informational.

Choice rules

Rules relate choices across groups:

  • requires: the choice is only valid when at least one of its required targets is selected. Targets are grouped by the target's option group: within one target group the relation is OR (any one target satisfies it), across target groups it is AND (each group's requirement must be satisfied).
  • excludes: the choice cannot be combined with the target choice.

The schema exposes each choice's requires and excludes arrays as lists of choice IDs so the UI can disable invalid combinations up front. The quote endpoint enforces the same rules server-side and returns 422 with a human-readable message on violation.

Price matrices

When a product has dimensions, its base price comes from a price matrix: a named 2D lookup table of dimension ranges to prices. Each matrix has a set of conditions, choice IDs that must all be selected for the matrix to be eligible. A matrix with no conditions is always eligible.

Matrix selection: among all eligible matrices (those whose entire condition set is contained in the buyer's selection), the one with the most conditions wins. Ties are broken by creation order (oldest first). Inside the winning matrix, the row whose dim1 and dim2 ranges contain the buyer's dimension values supplies the base price. Dimension values map to dim1 and dim2 in dimension position order.

If no matrix is eligible, or the winning matrix has no row covering the dimension values, the quote fails with a 422 not_priceable error. A product with no dimensions skips matrices entirely and uses its own price_amount as the base; if that is null, the quote is likewise not_priceable.

Pricing walkthrough

Given a valid selection, the total is computed in two passes, all in integer minor units, with PHP_ROUND_HALF_UP rounding to whole minor units at each percentage or area step:

  1. Resolve the base price (matrix row or product price). The breakdown starts with a line labeled Base.
  2. Pass 1, in group and choice position order: every selected choice that is not percent_of_total adds its delta (fixed, per_area, or percent of the base). Each choice adds one breakdown line labeled {Group name}: {Choice name}, including zero-delta choices, which appear with amount 0.
  3. Pass 2: each selected percent_of_total choice adds pass-1 subtotal * delta_value / 10000, computed against the same subtotal independently, so multiple percent_of_total choices do not compound each other. Each adds its own breakdown line.

The response's total equals the base plus all deltas. The currency is the product's currency.

Money and units summary

  • All amounts are integers in minor units (cents), with the product's ISO currency code.
  • Percent values are stored times 100: divide delta_value by 10000 to get the fraction.
  • per_area rates are minor units per square meter; dimensions are entered in centimeters and area is dim1 * dim2 / 10000 square meters.
  • Dimension values in quote requests must be integers, in the dimension's unit.

Visibility guarantees

The schema only exposes active choices; soft-deleted and deactivated choices are filtered out. Dimension constraints, matrix conditions, and requires and excludes lists are pruned to the currently visible choice IDs, so every choice ID that appears anywhere in the schema is resolvable within that same schema.