Configurator
Configuration schema
Fetch the complete configuration schema of a configurable product.
View as MarkdownThe schema endpoint returns everything a storefront needs to render a configurator UI for one product: dimensions with their per-choice range constraints, the product's price matrices (for hinting which matrix applies), and the option groups with their choices, price deltas, cross-choice rules, images, and requires_quote flags. See the chapter introduction for how these concepts fit together.
The schema endpoint returns everything a storefront needs to render a configurator UI for one product: dimensions with their per-choice range constraints, the product's price matrices (for hinting which matrix applies), and the option groups with their choices, price deltas, cross-choice rules, images, and requires_quote flags. See the chapter introduction for how these concepts fit together.
GET /v1/api/commerce/configurator/products/{id}/schema
Returns the complete configuration schema for one active configurable product. Everything in the response is pruned to currently available choices: a choice appears only when it is active, not deleted, and every one of its requires target groups still holds at least one available choice (pruning cascades, so a choice that requires an unavailable choice disappears too). Dimension constraints and requires and excludes lists reference only those available choice IDs, and a price matrix conditioned on a choice that is not available is omitted from price_matrices entirely. Returns 404 for any product that is not both status = active and capability = made_to_measure.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | Yes | Product ID. Must be an active product with capability made_to_measure. |
Response fields
The data object has three keys.
data.dimensions (array, in position order; the order defines the mapping to price matrix axes dim1 and dim2):
| Name | Type | Required | Description |
|---|---|---|---|
| key | string | always | Request key to use for this dimension in quote calls. |
| label | string | always | Display label. |
| unit | string | always | Display unit string, for example "cm". |
| min | integer | always | Minimum allowed value before constraint tightening. |
| max | integer | always | Maximum allowed value before constraint tightening. |
| constraints | array | always | Per-choice range tightenings; empty array when none. Each item: choice_id (uuid), min (integer or null), max (integer or null). When that choice is selected, a non-null min raises the effective minimum and a non-null max lowers the effective maximum. |
data.price_matrices (array, in creation order):
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | always | Matrix ID. Echoed back by the quote endpoint as matrix.id when this matrix supplied the base price. |
| name | string | always | Matrix name. |
| conditions | array of string (uuid) | always | Choice IDs that must all be selected for the matrix to be eligible. Empty array means always eligible. Among eligible matrices, the one with the most conditions wins. Matrices conditioned on a choice that is not available are not listed at all, so this array is never shortened relative to what the engine requires. |
data.option_groups (array, in position order):
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | always | Group ID. |
| name | string | always | Group name. Also used as the prefix of quote breakdown labels ("Group: Choice"). |
| type | string | always | single (at most one choice selectable) or multi (any number). |
| required | boolean | always | Whether at least one choice from this group must be selected. |
| choices | array | always | Choices in position order, see below. |
data.option_groups[].choices[]:
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | always | Choice ID. Send this in the quote request's choices array. |
| name | string | always | Choice name. |
| delta_type | string | always | fixed, per_area, percent, or percent_of_total. |
| delta_value | integer | always | Minor units for fixed; minor units per square meter for per_area; percent times 100 for percent and percent_of_total (1050 = 10.50 percent). |
| is_default | boolean | always | UI hint for the initial selection. |
| requires | array of string (uuid) | always | Choice IDs this choice requires. OR within one target group, AND across target groups. |
| excludes | array of string (uuid) | always | Choice IDs this choice cannot be combined with. |
| images | array | always | [{url, alt}]; empty array when the choice has no media. |
| requires_quote | boolean | always | When true, the storefront should route the buyer to a manual quote request instead of checkout. Informational: the quote endpoint still prices the configuration. |
Errors
| Status | Body | Cause |
|---|---|---|
| 401 | {"message":"Unauthenticated."} | Missing or invalid API key. |
| 403 | {"message":"Invalid ability provided."} | Key carries neither the catalog:read nor the commerce:read scope. |
| 403 | {"error":"Module not enabled: configurator"} | Tenant does not have the configurator module enabled. Note the error key instead of message. |
| 403 | {"message":"Tenant suspended.","code":"tenant_suspended"} | Tenant account is suspended. |
| 404 | {"message":"..."} | Product does not exist, is not active, or its capability is not made_to_measure. |
| 429 | {"message":"Too Many Attempts."} | Rate limit of 120 requests per minute exceeded. |
curl "https://api.borkol.com/v1/api/commerce/configurator/products/9c2f1e6a-8b4d-4f2a-9e11-3d5a7b9c0e21/schema" \
-H "Authorization: Bearer {api_key}" \
-H "Accept: application/json"const res = await fetch(
`https://api.borkol.com/v1/api/commerce/configurator/products/${productId}/schema`,
{
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "application/json",
},
}
);
const { data } = await res.json();{
"data": {
"dimensions": [
{
"key": "width",
"label": "Width",
"unit": "cm",
"min": 200,
"max": 700,
"constraints": [
{
"choice_id": "c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c",
"min": null,
"max": 600
}
]
},
{
"key": "depth",
"label": "Depth",
"unit": "cm",
"min": 150,
"max": 400,
"constraints": []
}
],
"price_matrices": [
{
"id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
"name": "Glass roof",
"conditions": ["c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c"]
},
{
"id": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9",
"name": "Base",
"conditions": []
}
],
"option_groups": [
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Roof type",
"type": "single",
"required": true,
"choices": [
{
"id": "c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c",
"name": "Glass",
"delta_type": "fixed",
"delta_value": 0,
"is_default": true,
"requires": [],
"excludes": ["f6a7b8c9-d0e1-4f2a-b3c4-d5e6f7a8b9c0"],
"images": [
{
"url": "https://cdn.borkol.com/t/9c2f/glass-roof.jpg",
"alt": "Glass roof"
}
],
"requires_quote": false
}
]
},
{
"id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"name": "LED lighting",
"type": "multi",
"required": false,
"choices": [
{
"id": "f6a7b8c9-d0e1-4f2a-b3c4-d5e6f7a8b9c0",
"name": "LED set (6)",
"delta_type": "per_area",
"delta_value": 2500,
"is_default": false,
"requires": ["c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c"],
"excludes": [],
"images": [],
"requires_quote": false
}
]
}
]
}
}GET /v1/api/commerce/configurator/products/{id}/schema
Returns the complete configuration schema for one active configurable product. Everything in the response is pruned to currently available choices: a choice appears only when it is active, not deleted, and every one of its requires target groups still holds at least one available choice (pruning cascades, so a choice that requires an unavailable choice disappears too). Dimension constraints and requires and excludes lists reference only those available choice IDs, and a price matrix conditioned on a choice that is not available is omitted from price_matrices entirely. Returns 404 for any product that is not both status = active and capability = made_to_measure.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | Yes | Product ID. Must be an active product with capability made_to_measure. |
Response fields
The data object has three keys.
data.dimensions (array, in position order; the order defines the mapping to price matrix axes dim1 and dim2):
| Name | Type | Required | Description |
|---|---|---|---|
| key | string | always | Request key to use for this dimension in quote calls. |
| label | string | always | Display label. |
| unit | string | always | Display unit string, for example "cm". |
| min | integer | always | Minimum allowed value before constraint tightening. |
| max | integer | always | Maximum allowed value before constraint tightening. |
| constraints | array | always | Per-choice range tightenings; empty array when none. Each item: choice_id (uuid), min (integer or null), max (integer or null). When that choice is selected, a non-null min raises the effective minimum and a non-null max lowers the effective maximum. |
data.price_matrices (array, in creation order):
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | always | Matrix ID. Echoed back by the quote endpoint as matrix.id when this matrix supplied the base price. |
| name | string | always | Matrix name. |
| conditions | array of string (uuid) | always | Choice IDs that must all be selected for the matrix to be eligible. Empty array means always eligible. Among eligible matrices, the one with the most conditions wins. Matrices conditioned on a choice that is not available are not listed at all, so this array is never shortened relative to what the engine requires. |
data.option_groups (array, in position order):
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | always | Group ID. |
| name | string | always | Group name. Also used as the prefix of quote breakdown labels ("Group: Choice"). |
| type | string | always | single (at most one choice selectable) or multi (any number). |
| required | boolean | always | Whether at least one choice from this group must be selected. |
| choices | array | always | Choices in position order, see below. |
data.option_groups[].choices[]:
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | always | Choice ID. Send this in the quote request's choices array. |
| name | string | always | Choice name. |
| delta_type | string | always | fixed, per_area, percent, or percent_of_total. |
| delta_value | integer | always | Minor units for fixed; minor units per square meter for per_area; percent times 100 for percent and percent_of_total (1050 = 10.50 percent). |
| is_default | boolean | always | UI hint for the initial selection. |
| requires | array of string (uuid) | always | Choice IDs this choice requires. OR within one target group, AND across target groups. |
| excludes | array of string (uuid) | always | Choice IDs this choice cannot be combined with. |
| images | array | always | [{url, alt}]; empty array when the choice has no media. |
| requires_quote | boolean | always | When true, the storefront should route the buyer to a manual quote request instead of checkout. Informational: the quote endpoint still prices the configuration. |
curl "https://api.borkol.com/v1/api/commerce/configurator/products/9c2f1e6a-8b4d-4f2a-9e11-3d5a7b9c0e21/schema" \
-H "Authorization: Bearer {api_key}" \
-H "Accept: application/json"const res = await fetch(
`https://api.borkol.com/v1/api/commerce/configurator/products/${productId}/schema`,
{
headers: {
Authorization: `Bearer ${apiKey}`,
Accept: "application/json",
},
}
);
const { data } = await res.json();{
"data": {
"dimensions": [
{
"key": "width",
"label": "Width",
"unit": "cm",
"min": 200,
"max": 700,
"constraints": [
{
"choice_id": "c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c",
"min": null,
"max": 600
}
]
},
{
"key": "depth",
"label": "Depth",
"unit": "cm",
"min": 150,
"max": 400,
"constraints": []
}
],
"price_matrices": [
{
"id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
"name": "Glass roof",
"conditions": ["c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c"]
},
{
"id": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9",
"name": "Base",
"conditions": []
}
],
"option_groups": [
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Roof type",
"type": "single",
"required": true,
"choices": [
{
"id": "c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c",
"name": "Glass",
"delta_type": "fixed",
"delta_value": 0,
"is_default": true,
"requires": [],
"excludes": ["f6a7b8c9-d0e1-4f2a-b3c4-d5e6f7a8b9c0"],
"images": [
{
"url": "https://cdn.borkol.com/t/9c2f/glass-roof.jpg",
"alt": "Glass roof"
}
],
"requires_quote": false
}
]
},
{
"id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"name": "LED lighting",
"type": "multi",
"required": false,
"choices": [
{
"id": "f6a7b8c9-d0e1-4f2a-b3c4-d5e6f7a8b9c0",
"name": "LED set (6)",
"delta_type": "per_area",
"delta_value": 2500,
"is_default": false,
"requires": ["c1a2b3c4-d5e6-4f7a-8b9c-0d1e2f3a4b5c"],
"excludes": [],
"images": [],
"requires_quote": false
}
]
}
]
}
}Errors
| Status | Body | Cause |
|---|---|---|
| 401 | {"message":"Unauthenticated."} | Missing or invalid API key. |
| 403 | {"message":"Invalid ability provided."} | Key carries neither the catalog:read nor the commerce:read scope. |
| 403 | {"error":"Module not enabled: configurator"} | Tenant does not have the configurator module enabled. Note the error key instead of message. |
| 403 | {"message":"Tenant suspended.","code":"tenant_suspended"} | Tenant account is suspended. |
| 404 | {"message":"..."} | Product does not exist, is not active, or its capability is not made_to_measure. |
| 429 | {"message":"Too Many Attempts."} | Rate limit of 120 requests per minute exceeded. |