Configuration API Guide — Project-based Integration Flow
Welcome to the decoloop CPQ API 2.0 Project-based Integration Flow. This guide explains the Project-centric approach: an architecture where a full Project is created upfront on the server and used to manage all configured items, or Polygons, within that project session.
This approach is best suited for custom-tailored environments, professional room or house planners, B2B sales portals, and integrations where draft states, multiple configured items, quote review, and project-level customer/order data are naturally represented by a backend Project.
NOTE: This documentation is for the 2.0 version of the API. This version is not backwards compatible with the previous Gateway API.
Project-centric architecture
In the Project-based flow, the client starts by creating a server-side Project. Every configured item is then added to that Project as a Polygon.
The API keeps the Project state on the server. After each change, the client can either retrieve the full updated Project or apply JSON Patch operations to a local copy. This makes the flow useful for sessions where the user is working inside a project, quote, room plan, house plan, or sales consultation.
Use this guide when:
- the frontend session should map directly to one backend
Project; - users can configure multiple items as part of the same quote or plan;
- draft, edit, cancel-edit, and completion states should be stored on the backend project;
- project-level customer, delivery, sales, or quote information is known before checkout;
- the integration resembles a professional planner or B2B sales portal.
If your integration configures individual items independently in a webshop/cart and only creates a Project at checkout, use the Standalone Polygon Guide instead.
JavaScript/TypeScript Library
We have published a JavaScript/TypeScript library on NPM with 2 clients and a set of helper functions. You can find it here. It's also possible to generate your own client by downloading the OpenAPI specification and using a tool like https://editor.swagger.io/.
Authentication
The decoloop CPQ API uses JWT bearer tokens for authenticated requests. To create a token, use the CreateToken method. The response contains a token which can be used in an Authorization header like so:
Authorization: bearer {access_token}When using one of the provided clients this will look something like this:
import { WoonTotaalFetchClient } from '@sieval/woontotaal-client/fetch';
const client = new WoonTotaalFetchClient('https://woontotaal.example.com');
// retrieve token using provided credentials
const token = await client.auth_CreateToken({
// the api key is optional and provided by the owner of the WoonTotaal instance
apiKey: '00000000-0000-0000-0000-000000000000',
domainName: 'store_1',
password: 'test',
username: 'employee_a',
});
// set access token, the client will now automatically append it to the headers of any call
client.setConfig({ bearerToken: token.access_token });Impersonation
If a user has enough rights, impersonation can be used to make API calls for other Licensees/Employees. This means calls can be made for other branches. A set of four HTTP headers is available for this functionality:
- WT-Impersonation-LicenseeId
Value can contain a
Licensee.Idfrom the WT database. - WT-Impersonation-DomainName
Value can contain a
Licensee.DomainNamefrom the WT database. ADomainNamecan contain for example a store number or company name. - WT-Impersonation-EmployeeId
Value can contain a
Employee.Idfrom the WT database. - WT-Impersonation-Username
Value can contain a
Employee.Usernamefrom the WT database.
NOTE: These headers need to be present for each call you want to use impersonation with.
Diff (Patch) Response
v2.0 comes in two flavours:
- full
Projectresponse - JSON diff patch
This means that almost every API call has 2 variants. One always returns the full Project and one returns an array of operations to patch an existing JSON. More info JSON patches can be found in the JSON Patch RFC6902.
How to Use JSON Patches
JSON patches contain changes between 2 JSONs. This allows us to only send changes to the client. In order to apply these changes, first a full Project response is needed. This full response is usually retrieved by calling the Project/Get or Project/Create methods.
The @sieval/woontotaal-client NPM package contains a helper function, which takes care of patching. The below example creates a new Project and adds an empty Polygon (note: the example doesn't include authentication):
import { WoonTotaalFetchClient } from '@sieval/woontotaal-client/fetch';
import { patchProject } from '@sieval/woontotaal-client';
const client = new WoonTotaalFetchClient('https://woontotaal.example.com');
// create a new project, this will return a full Project response
let project = await client.project_Create();
// logs an empty array
console.log(project.polygons);
let operations = await client.polygon_AddDiff({
projectId: project.id,
polygonTypeId: null,
modelId: null,
materialId: null,
materialCode: null,
height: null,
width: null,
});
// apply the operations to the project object
patchProject(project, operations);
// logs an array with 1 polygon
console.log(project.polygons);When to use the Project-based flow
Choose this flow when the user journey is naturally centered around a backend Project and configurations need to be able to react to each other.
For example to create sets of configurations for outlining patterns, equal tube sizes for roller blinds, etc.
For webshops where each cart line is configured independently and only grouped into an order at checkout, use the Standalone Polygon Guide.
Localization
If the decoloop CPQ configuration contains multiple languages, a language header can be used to set the request language: WT-Request-Language. The value needs to be a 2 letter language code, for example: NL, EN, FR, DE.
WT-Request-Language: NLNOTE: In order to use a different request language than the default, the language needs to be present and active in the WoonTotaal database.
Project-based configuration flow
In this flow, configuration starts from a server-side Project. Each configured item is added to that project as a Polygon.
Before we go into the flow, first let's clear up some terms and basic decoloop CPQ principles.
Terms
- Material: part of a end product, for example a fabric for a curtain
- Project: the server-side container for a project session, quote, room/house plan, or multi-item configuration
- Polygon: one configured item inside a
Project - CategoryLink: part of a product, for example for a curtain: main fabric, lining, borders
- CategoryLinkMaterial: information about a
Materialapplied to aCategoryLink - Property: property of a product, basically represents a question which requires an answer
- ListValue: selectable option for a
Property - Model: manufacturing method for a product, for example for a curtain: wave, pleated, eyelets
- Wizard: configurator steps which should be displayed for a certain product
Configuration Basics
When configuring a Polygon a few basic rules apply to every configuration:
- the
Polygonneeds aModel - the
Polygonneeds at least oneMaterial - the
Polygonneeds to have valid required dimensions
After this, decoloop CPQ can start calculating and return a list of Properties.
Before starting, make sure you have:
- instantiated a client
- retrieved an access token for authorization
Entry Point
A configurator can have two entry points:
- Model — Retrieve a list of all
Models, select one, and ask decoloop CPQ for a list of validMaterialsfor the selectedModel. - Material — Retrieve a list of valid
Modelsfor thisMaterialby sending the SKU/article code to decoloop CPQ
Configuration Steps
1. Retrieve all Models
When retrieving all Models, decoloop CPQ returns a possibly nested menu. For example:
- Curtain
- Curtain, Pleated
- Curtain, Wave
- Horizontal blind
- Horizontal blind, Wood
- Horizontal blind, Aluminum
const models = await client.model_GetAll();2. Create a Project
Before configuring items in the Project-based flow, create a new server-side Project. This Project becomes the shared context for all Polygons configured during the session.
let project = await client.project_Create();3. Add a Polygon to the Project
We can now add a Polygon to the Project. A Polygon represents one configured item. It can be created empty or initialized with preselected values such as model, material, dimensions, or polygon type.
let operations = await client.polygon_AddDiff({
projectId: project.id,
// optional Model.Id, initialises the Polygon with this Model
modelId: null,
// optional Material.Code or Material.Id
materialCode: null,
materialId: null,
// optional PolygonType.Id
polygonTypeId: null,
// optional width and height to initialise the Polygon with
width: null,
height: null,
});
// patch the created Project
patchProject(project, operations);
// a new Polygon is always added as last in the array
let polygon = project.polygons[0];4. Set Model
Using the list of Models we retrieved in step 1, we can now set the Model on the newly created Polygon.
// always make sure the selected model does not have any items in its children
// only models without children are valid for selection
const model = models[0];
if (model.children.length) {
throw new Error("Invalid model");
}
operations = await client.polygon_SetModelDiff({
modelId: model.id,
polygonId: polygon.id,
});
// patch the Project
patchProject(project, operations);
// refresh the Polygon variable
polygon = project.polygons[0];
console.log(polygon.modelDescription);5. Set a Material
Now that we have set the Model, we know which CategoryLinks are available/required for this product.
const categoryLink = polygon.categoryLinks[0];
const pagedResult = await client.material_Browse({
categoryId: null,
categoryLinkId: categoryLink.id,
codeFilter: null,
colorFilter: null,
descriptionFilter: null,
pageNumber: 1,
pageSize: 25,
});
const material = pagedResult.items[0];
// apply the Material
operations = await client.polygon_SetMaterialDiff({
categoryLinkId: categoryLink.id,
materialId: material.id,
polygonId: polygon.id,
});
patchProject(project, operations);
polygon = project.polygons[0];Using an article code/SKU:
operations = await client.polygon_SetMaterialByCodeDiff({
categoryLinkId: categoryLink.id,
materialCode: sku,
polygonId: polygon.id,
});6. Set Dimensions
Now we are ready to set the width/height (in cm) of the Polygon.
operations = await client.polygon_SetDimensionsDiff({
polygonId: polygon.id,
width: 100,
height: 100,
});
patchProject(project, operations);
polygon = project.polygons[0];
// log the price of the Polygon
console.log(polygon.prices.totalPrice);NOTE: the dimensions should always be set in centimeters.
7. Set Property Value
decoloop CPQ will now return a list of Properties for each CategoryLink. These Properties are questions which should be asked to the user.
const categoryMaterial = polygon.categoryLinks[0].categoryLinkMaterial;
categoryMaterial.properties.forEach(prp => {
console.log(prp.displayLabel);
});
// set a property value
const property = categoryMaterial.properties[0];
const listValue = property.listValues[0];
operations = await client.polygon_SetPropertyValueDiff({
polygonId: polygon.id,
categoryLinkMaterialPropertyId: property.id,
value: listValue.value,
});
patchProject(project, operations);
polygon = project.polygons[0];Wizard (Optional)
If the Wizard functionality is configured for this decoloop CPQ instance and account, we can use it to build configurator steps.
// retrieve the Wizard steps for the current product
const steps = await client.wizard_GetStepsForProduct(polygon.productCategoryId);
// use the helper function to build the steps
const configuratorSteps = buildConfiguratorSteps(selectedPolygon, steps);
configuratorSteps.forEach(step => {
const isValid = step.isValid;
step.properties.forEach(prp => {
const propertyIsValid = prp.isValid;
const messages = prp.messages;
});
});Polygon.State
The Polygon.State property indicates the current configurator state of a Polygon. Possible values are: New, Completed, Editing.
- When you are done configuring the
Polygon, it has to be set toCompleteusing Polygon/Complete or Polygon/CompleteDiff. - When a
Polygonis complete but needs to be edited, first call Polygon/Edit or Polygon/EditDiff. This call will also create a snapshot of theProjectbefore editing.
Apply changes: Call Polygon/Complete or Polygon/CompleteDiff.
Cancel changes: Call Polygon/CancelEdit or Polygon/CancelEditDiff. This will restore the snapshot which was made while calling the Polygon/Edit method.
Next Steps
- Use the Standalone Polygon Guide for webshop/cart-style integrations.
- View the full API Reference.