Documentation / Configurators

Configurators

The Configurator object — controls, calculations, parts and presets — how a configurator prices a product, test calculations, and the public showcase of shareable configurators.

A configurator is a pricing engine for one kind of product. The customer fills in controls (width, fabric, colour), and calculations turn those values into quantities of materials, i.e. a priced bill of materials. Parts optionally produce a production cut-list, and presets are ready-made configurations.

A configurator is what the widget shows to customers. A priced result saved to an order becomes a product.

The structure is stored as JSON and is not validated beyond formula syntax. A misspelled field does not cause an error; the line it belongs to simply prices at 0. Always check a configurator with Calculate Configurator and read its warnings.

Field types follow the field notation.

Access

Project endpoints require both the projects and configurators OAuth scopes. The user must be a member of the project, and the owner's subscription must be active. Otherwise they return 404 RES_NOT_FOUND. The project owner bypasses permission checks.

Permission Grants
can_view_configurators Read configurators and run test calculations
can_edit_configurators Create, update and delete configurators

Material prices inside a configurator follow the material price permissions: a tier the user may not see is returned as 0.

Showcase endpoints require only the configurators scope, except copying, which also needs projects.

Configurator Object

Configurator Structure

Field Type Description
uuid uuid Configurator ID
name string Name, 1–255 characters
product_name string Template for the name of each priced product, see Product name template
product_unit string Unit of the finished product, usually pcs
product_work_cost decimal Flat amount added to the sale and dealer totals
product_amortization decimal Another flat amount added to the sale and dealer totals
price_multiplier decimal Multiplies every material price; 1 means no change
controls array of control objects Fields the customer fills in
calculations array of calculation objects Priced bill of materials
parts array of part objects Production cut-list, not priced
presets array of preset objects Ready-made configurations
materials array of material objects Materials linked to the configurator. Only in Get Configurator
is_shareable boolean Listed in the public showcase so other Configo users can copy it
is_publicly_accessible boolean Can be opened by a direct link
is_calculations_displayed boolean Show the material breakdown to the customer in the widget
status string active or suspended
image_uuid ?uuid Cover picture file
image_url ?string Relative URL of the picture, e.g. /uploads/{uuid}
created_at ISO8601 datetime When the configurator was created
updated_at ISO8601 datetime When the configurator was last changed

Prices are rounded to the project's price_step: material lines to cents, totals to the step.

Control Structure

{
  "id": "$width",
  "type": "number",
  "label": "Width, mm",
  "default": 550,
  "value": "",
  "class": "col-span-6",
  "source": "",
  "dataset": "",
  "order": 1,
  "min": 150,
  "max": 2500,
  "step": 1
}
Field Type Description
id string Must start with $, then ASCII letters, digits or _, e.g. $width. Formulas refer to controls by this id
type string See the table below. An unknown type renders as a text input
label string Shown to the customer
default any Initial value. For select types it must match an option exactly; a value matching nothing selects the first option. "" leaves a select unset
value string Runtime state; always saved as ""
class string Width in a 12-column grid: col-span-1 … col-span-12
order integer Display order, from 1
source string Where options come from; meaning depends on type
dataset any Options or a lookup key; meaning depends on type
min?, max?, step? number For number. min/max are enforced by the widget only, not by calculations
rows? number For textarea
type source dataset Value produced
number, text, textarea — — What the customer typed
select json or yaml A JSON string of an array, e.g. "[\"A\",\"B\"]" The chosen string
select group A material group name The chosen material's name
select A material group name A property key That property's distinct values across the group
select A material uuid A property key whose value is an array One entry of that array
select_image — A material group name The chosen material's name, with its picture shown
select_depend — [{"property", "variable"}] Name of a material whose property equals the value of control variable; several entries are ANDed
materials groups Array of group names {<material uuid>: {name, unit, quantity}}: the customer ticks materials and enters quantities

A materials control is billed directly from its value and needs no calculation entry.

Calculation Structure

One calculation produces at most one line of the bill of materials.

{
  "material": "Fabric",
  "formulas": [
    {
      "code": "$width * (max(1400, $height) + 200) / 1000000",
      "variable": "$cloth",
      "conditions": []
    }
  ]
}
Field Type Description
material string A material uuid (always that material) or a material group name (whichever the customer picked)
formulas array Up to 15 formulas, tried in order. The first whose conditions hold, whose material resolves and whose result is > 0 wins

Formula Structure

Field Type Description
code string An expression giving the quantity in the material's own unit. A constant like "2" is fine
variable string For a group material: id of the control that picks the material. "" for a uuid material
conditions array Up to 10 conditions, all of which must hold. [] means always

There is no OR between conditions. To express OR, repeat the formula with different conditions; the first match wins.

Condition Structure

Field Type Description
variable string Control id
operator string equal, not_equal, greater, less, include, not_include
value any Value to compare with. For a group-backed control, this is the material's name

Part Structure

A cut-list row. Parts never affect the price.

{
  "material": "<material uuid>",
  "formulas": [
    {
      "sizes": 2,
      "code": [
        "$width - 43",
        "$height",
        ""
      ],
      "unit": "mm",
      "item": {
        "quantity": 1,
        "unit": "pcs"
      },
      "conditions": []
    }
  ]
}
Field Type Description
material uuid Material the row is labelled with
formulas[].sizes integer 1, 2 or 3 dimensions
formulas[].code array Always three expressions; only the first sizes are used, the rest ""
formulas[].unit string Unit of the dimensions, usually mm
formulas[].item object {quantity, unit}: fixed piece count
formulas[].conditions array As in calculations

Preset Structure

Field Type Description
name string Shown to the customer
configuration object Control values, e.g. {"$width": 550, "$height": 1400}

Formulas

Formulas use a dedicated expression language, not JavaScript:

  • Arithmetic + - * / % **, comparisons (== is loose, === strict), && || ?? !, ? :.
  • Functions: abs round ceil floor trunc max min pow sqrt exp log log10 sin cos tan asin acos atan atan2 random lower upper trim. Constants: PI E LN2 LN10 SQRT2 SQRT1_2. There is no property access and no Math. prefix.
  • $id is replaced by the control's value. Values from the form can be strings, so "1200" + 1 concatenates; write +$width + 1.
  • A $id that matches no control makes the whole formula evaluate to 0. It does not cause an error; it shows up in warnings.

Units are never converted. Controls are usually in millimetres and prices per square or running metre, so divide: ($width / 1000) * ($height / 1000) for m².

Every formula and the product name template are parsed when saved. One that fails to parse rejects the request with 400 CONFIGURATOR_FORMULA_INVALID, see Errors.

Product name template

product_name is text with two kinds of placeholders. A bare $control is substituted as is, and a ${...} block is evaluated as an expression. An untouched control reads as the literal [none].

${'$system' == 'Uni' ? 'Closed PVC' : '$system'}; fabric - ${lower('$cloth')}; $width×$height mm

Price

sale total     = Σ(quantity × (1 + percent_waste/100) × sale_price × price_multiplier) + product_work_cost + product_amortization
dealer total   = same with dealer_price
purchase total = Σ(quantity × (1 + percent_waste/100) × purchase_price × price_multiplier)

List Configurators

GET /api/v1/projects/{project.uuid}/configurators

Returns the project's configurators, most recently updated first, without the structure fields (controls, calculations, parts, presets), prices and materials.

Requires can_view_configurators.

Query String Params

Field Type Description
status? string active or suspended

Get Configurator

GET /api/v1/projects/{project.uuid}/configurators/{configurator.uuid}

Returns the full configurator, including its linked materials.

Requires can_view_configurators.

Create Configurator

POST /api/v1/projects/{project.uuid}/configurators

Requires can_edit_configurators.

JSON Params

Field Type Description
name string 1–255 characters
product_name? string Up to 512 characters. Default ""
product_unit? string Up to 64 characters. Default pcs
product_work_cost? decimal ≥ 0. Default 0
product_amortization? decimal ≥ 0. Default 0
price_multiplier? decimal ≥ 0. Default 1
controls? array Default []
calculations? array Default []
parts? array Default []
presets? array Default []
material_uuids? array of uuid Materials of this project to link. A material that is not linked is invisible to calculations
is_shareable? boolean Default false. true lists the configurator in the public showcase
is_publicly_accessible? boolean Default false
is_calculations_displayed? boolean Default true
status? string Default active
image_uuid? ?uuid Cover picture

Create the materials first, then the configurator with all of them in material_uuids.

Example Request

{
  "name": "Roller blind",
  "product_name": "Roller blind $cloth, $width×$height mm",
  "is_shareable": false,
  "controls": [
    {
      "id": "$width",
      "type": "number",
      "label": "Width, mm",
      "default": 550,
      "value": "",
      "class": "col-span-6",
      "source": "",
      "dataset": "",
      "order": 1,
      "min": 150,
      "max": 2500,
      "step": 1
    },
    {
      "id": "$height",
      "type": "number",
      "label": "Height, mm",
      "default": 1400,
      "value": "",
      "class": "col-span-6",
      "source": "",
      "dataset": "",
      "order": 2,
      "min": 150,
      "max": 2600,
      "step": 1
    },
    {
      "id": "$cloth",
      "type": "select",
      "label": "Fabric",
      "default": "Blackout 605",
      "value": "",
      "class": "col-span-12",
      "source": "group",
      "dataset": "Fabric",
      "order": 3
    }
  ],
  "calculations": [
    {
      "material": "Fabric",
      "formulas": [
        {
          "code": "$width * (max(1400, $height) + 200) / 1000000",
          "variable": "$cloth",
          "conditions": []
        }
      ]
    },
    {
      "material": "0199a2c1-5d6e-7f70-8182-93a4b5c6d7e8",
      "formulas": [
        {
          "code": "$width / 1000",
          "variable": "",
          "conditions": []
        }
      ]
    }
  ],
  "material_uuids": [
    "0199a2c0-7a1b-7c2d-8e3f-a0b1c2d3e4f5",
    "0199a2c1-5d6e-7f70-8182-93a4b5c6d7e8"
  ]
}

Response

201 Created

{
  "uuid": "0199ab16-3d4e-7f50-8162-738495061728"
}

Modify Configurator

PATCH /api/v1/projects/{project.uuid}/configurators/{configurator.uuid}

Updates only the fields you send. controls, calculations, parts, presets and material_uuids each replace the whole list when sent. At least one field must be present. Only the formulas you send are validated.

On update, price_multiplier below 1 is silently replaced with 1.

Requires can_edit_configurators.

Response

204 No Content

Replace Configurator

PUT /api/v1/projects/{project.uuid}/configurators/{configurator.uuid}

Same as Modify Configurator, but every field of Create Configurator is required. image_uuid may be null.

Delete Configurator

DELETE /api/v1/projects/{project.uuid}/configurators/{configurator.uuid}

Deletes the configurator. Its materials and the products already made with it stay.

Requires can_edit_configurators.

Response

204 No Content

Calculate Configurator

POST /api/v1/projects/{project.uuid}/configurators/{configurator.uuid}/calculate

Runs the saved configurator with the given control values and returns the priced result. Price tiers the user's role may not see (can_view_purchase_price, can_view_dealer_price, can_view_sale_price) count as 0 in every line and total. Nothing is written. Use it to test a configurator after changing it.

Requires can_view_configurators.

JSON Params

Field Type Description
values? object Control values keyed by control id, including the $. Controls not given use their default

Example Request

For a configurator with only the fabric line from the example above:

{
  "values": {
    "$width": 1200,
    "$height": 1600,
    "$cloth": "Blackout 605"
  }
}

Response

200 OK

Field Type Description
product_name string The rendered product name template
price_totals object {purchase, sale, dealer}, rounded to the project's price step
calculated_materials array Bill of materials lines, see below
calculated_parts array Cut-list rows: {name, quantity, unit, size, size_unit}, size like "1157×1600"
warnings array of string Problems found: a calculation that matched no material, conditions that never held, formulas that evaluated to 0, values for unknown controls. An empty array is the only sign that every calculation contributed a line

A calculated_materials line:

Field Type Description
uuid uuid Material ID
name string Material name
unit string Material unit
quantity string Quantity including waste, three decimals, e.g. "1.932"
price object Unit price per tier, {purchase, sale, dealer}, with price_multiplier applied
amount object price × quantity per tier
{
  "product_name": "Roller blind Blackout 605, 1200×1600 mm",
  "price_totals": {
    "purchase": 9.53,
    "sale": 22.45,
    "dealer": 17.01
  },
  "calculated_materials": [
    {
      "uuid": "0199a2c0-7a1b-7c2d-8e3f-a0b1c2d3e4f5",
      "name": "Blackout 605",
      "unit": "sm",
      "quantity": "2.268",
      "price": {
        "purchase": 4.2,
        "sale": 9.9,
        "dealer": 7.5
      },
      "amount": {
        "purchase": 9.53,
        "sale": 22.45,
        "dealer": 17.01
      }
    }
  ],
  "calculated_parts": [],
  "warnings": []
}

Showcase

Configurators with is_shareable: true and status: active, from all Configo projects, form a public catalog. Any user can browse it and copy a configurator into their own project. Prices are never exposed through the showcase.

List Showcase Configurators

GET /api/v1/showcase/configurators

Requires the configurators scope.

Query String Params

Field Type Description
search? string Name contains, case-insensitive
page? integer From 1. Default 1
limit? integer 1–50. Default 20

Response

200 OK — an array of {uuid, name, product_name, product_unit, is_calculations_displayed, updated_at}, most recently updated first.

Get Showcase Configurator

GET /api/v1/showcase/configurators/{configurator.uuid}

Returns the configurator's structure and materials. All prices, product_work_cost, product_amortization and price_multiplier are 0.

Requires the configurators scope.

Copy Showcase Configurator

POST /api/v1/showcase/configurators/{configurator.uuid}/copy

Copies the configurator and all its materials into one of the user's projects. The copy is named "<name> [showcase]", is not shareable, and gets new material uuids that calculations are remapped to. Prices are copied only if the user is also a member of the source project; otherwise they are 0.

Requires the projects and configurators scopes, and can_edit_configurators in the target project.

JSON Params

Field Type Description
project_uuid uuid Target project

Response

201 Created

{
  "uuid": "0199ab27-4e5f-7061-8273-849506172839"
}

Errors

Status Code When
400 REQ_VALIDATION_FAILED The body does not match the schema
400 REQ_NO_DATA_PROVIDED Modify only: the body has no fields
400 CONFIGURATOR_FORMULA_INVALID A formula or the product name template does not parse; see below
400 RES_NOT_FOUND A material in material_uuids or image_uuid does not belong to this project
403 AUTH_INSUFFICIENT_SCOPE The token lacks a required scope
403 AUTH_NO_PROJECT_ACCESS The user's role lacks can_view_configurators (read, calculate) or can_edit_configurators (write, copy)
404 RES_NOT_FOUND The project or configurator does not exist, or a showcase configurator is not shared

CONFIGURATOR_FORMULA_INVALID lists every problem in reason:

{
  "id": 4013,
  "code": "CONFIGURATOR_FORMULA_INVALID",
  "message": "A configurator formula or the product name template could not be parsed.",
  "reason": [
    "calculations[0].formulas[0]: Expected \")\"",
    "product_name: Unexpected character: ."
  ]
}