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 visible choices: only active, non-deleted choices appear, and dimension constraints, matrix conditions, and requires and excludes lists reference only those visible choice IDs. Returns 404 for any product that is not both status = active and type = configurable.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | Yes | Product ID. Must be an active product of type configurable. |
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. |
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 lacks the commerce:read scope. |
| 403 | {"error":"Module not enabled: commerce"} | Tenant does not have the commerce 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 type is not configurable. |
| 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 visible choices: only active, non-deleted choices appear, and dimension constraints, matrix conditions, and requires and excludes lists reference only those visible choice IDs. Returns 404 for any product that is not both status = active and type = configurable.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string (uuid) | Yes | Product ID. Must be an active product of type configurable. |
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. |
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 lacks the commerce:read scope. |
| 403 | {"error":"Module not enabled: commerce"} | Tenant does not have the commerce 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 type is not configurable. |
| 429 | {"message":"Too Many Attempts."} | Rate limit of 120 requests per minute exceeded. |