Decoloop CPQ Embedded Guide
Note: If your integration needs to route through
api.decoloop.comfor multiple CPQ applications, suppliers, or instances, see the Proxy-Based Embedded CPQ Guide.
Introduction
Decoloop CPQ can be embedded in a third-party website or application using an iframe. This allows users to configure CPQ products without leaving the host application, while the host application can retrieve the saved project and optionally send it to manufacturing through the Decoloop CPQ Gateway API.
This guide describes the recommended integration flow, iframe parameters, browser event handling, authentication, and the Embedded Gateway API endpoints.
Integration Flow
A typical embedded integration works as follows:
- Authenticate: The third-party application requests a bearer token from the Decoloop CPQ Gateway using its API key and credentials.
- Embed CPQ: The host application opens Decoloop CPQ in an iframe using the bearer token and
isEmbedded=true. - Configure project: The user configures one or more products in the embedded CPQ experience.
- Receive iframe event: When the user saves or closes a project, Decoloop CPQ sends a browser
messageevent to the parent window. The event payload contains the project ID. - Load project data: The host application calls
POST /api/Gateway/Embedded/LoadProjectwith the project ID to retrieve the saved project. - Send to manufacturing: If the user confirms the order, the host application calls
POST /api/Gateway/Embedded/SendProject.
Prerequisites
To connect to a Decoloop CPQ instance, third-party applications need an API key provided by Decoloop first. Please contact us through phone or email to obtain this key.
The API key is specific to the third-party application and can be reused across multiple CPQ instances when configured by Decoloop.
You also need:
- The base URL of the target Decoloop CPQ instance, for example
https://sandbox.decoloop.com. - A valid
domainName,username, andpassword. - Permission to use the Embedded Gateway API.
Step 1: Create a Token
Create a token using the Decoloop CPQ Gateway.
Endpoint: POST /token
Request
POST https://sandbox.decoloop.com/token
Content-Type: application/json
{
"domainName": "test",
"username": "test",
"password": "test",
"apiKey": "test-123-test"
}Response
{
"access_token": "aaabbbccc",
"token_type": "bearer",
".issued": "8-10-2018 15:39:41",
".expires": "2018-10-22T15:39:41.1959628+02:00",
"refresh_token": "dddeeefff"
}The access_token is used in two places:
- As the
tokenquery parameter when opening the embedded iframe. - As the bearer token for Embedded Gateway API calls.
Authentication for Embedded API Requests
All Embedded Gateway API endpoints require the bearer token returned by POST /token.
Send the token in the Authorization header:
Authorization: bearer aaabbbcccFor example:
GET https://sandbox.decoloop.com/api/Gateway/Embedded/GetAllSuppliers
Authorization: bearer aaabbbccc
Accept: application/jsonThe token determines which CPQ instance, licensee, language, supplier configuration, material catalog, and project data the caller can access. Do not assume that IDs are globally unique across tenants or CPQ instances.
Step 2: Create an Iframe
Create an iframe using the Decoloop CPQ URL and the required query parameters:
token: theaccess_tokenfrom Step 1.isEmbedded=true: enables embedded behavior.
Example iframe URL:
https://sandbox.decoloop.com?token=ACCESS_TOKEN&isEmbedded=trueExample HTML:
<iframe
src="https://sandbox.decoloop.com?token=ACCESS_TOKEN&isEmbedded=true"
width="100%"
height="800"
title="Decoloop CPQ"
></iframe>Step 3: Listen for Project Events
When a project is saved or closed, Decoloop CPQ sends a message to the parent window. The message contains the saved project ID.
window.addEventListener('message', event => {
// Always validate the event origin before trusting the payload.
if (event.origin !== 'https://sandbox.decoloop.com') {
return;
}
const data = event.data;
if (!data || typeof data !== 'object') {
return;
}
const projectId = data.payload;
switch (data.type) {
case 'CLOSE_PROJECT':
onCloseProject(projectId);
break;
case 'SAVE_PROJECT':
onSaveProject(projectId);
break;
}
});Event Types
| Event type | Description |
|---|---|
SAVE_PROJECT | The user saved a project. Use the project ID to load the full project data. |
CLOSE_PROJECT | The embedded CPQ experience was closed. The payload may contain the current project ID. |
Step 4: Load or Send the Project
After receiving a project ID from the iframe, use the Embedded Gateway API:
| Action | Endpoint |
|---|---|
| Load a saved project | POST /api/Gateway/Embedded/LoadProject |
| Send a project to manufacturing | POST /api/Gateway/Embedded/SendProject |
Embedded Gateway API
The following endpoints are available for embedded integrations.
All examples assume this base URL:
https://sandbox.decoloop.comAll examples also assume this authorization header:
Authorization: bearer ACCESS_TOKENLoad a Project
Loads a stored project by project ID.
Endpoint: POST /api/Gateway/Embedded/LoadProject
Use this endpoint after receiving a SAVE_PROJECT or CLOSE_PROJECT event from the iframe.
Request
POST https://sandbox.decoloop.com/api/Gateway/Embedded/LoadProject
Authorization: bearer ACCESS_TOKEN
Content-Type: application/json
Accept: application/json
{\n \"projectId\": 69623,\n \"includePurchasePrices\": false\n}Request Body
| Field | Type | Required | Description |
|---|---|---|---|
projectId | integer | Yes | ID of the project to load. This is usually the project ID received from the iframe event payload. |
includePurchasePrices | boolean or null | No | When true, purchase prices are included if the authenticated user has the required embedded rights. Use false unless purchase prices are explicitly needed. |
Successful Response
200 OK returns the full project.
{
"id": 69623,
"projectNumber": "0001.00482",
"reference": "Sandbox Customer",
"created": "2026-06-05T12:17:35.303Z",
"lastOpened": "2026-06-05T12:17:35.303Z",
"lastModified": "2026-06-05T12:21:58.31Z",
"projectStatus": 1,
"price": {
"totalPrice": 667.56,
"totalPriceBeforePromotions": 667.56,
"priceGroups": [
{
"code": "MATERIAL",
"description": "Material",
"displayOrder": 998,
"elements": [
{
"code": "1234",
"quantity": 15.07,
"description": "Fabric A",
"unit": "m",
"unitPriceBeforePromotion": 19.36,
"unitPrice": 19.36,
"totalPrice": 291.7552,
"totalPriceBeforePromotion": 291.7552,
"sbnid": 2,
"purchasePriceInfo": {
"unitPurchasePrice": 0.0,
"totalPurchasePrice": 0.0
}
}
]
},
{
"code": "WAGE",
"description": "Wage",
"displayOrder": 999,
"elements": [
{
"code": "C1234",
"quantity": 10.0,
"description": "NL1211: Weefstof band 8",
"unit": "bn",
"unitPriceBeforePromotion": 37.58,
"unitPrice": 37.58,
"totalPrice": 375.8,
"totalPriceBeforePromotion": 375.8,
"sbnid": 2,
"purchasePriceInfo": {
"unitPurchasePrice": 18.79,
"totalPurchasePrice": 187.9
}
}
]
}
],
"promotions": [],
"discountAmount": 0.0,
"totalPurchasePrice": 187.9
},
"polygons": [
{
"id": 95032,
"productCategoryId": 10001,
"modelCode": "1100",
"modelDescription": "Gordijn, Vliesband",
"width": 200.0,
"height": 200.0,
"categoryLinks": [
{
"id": 401,
"description": "Gordijn",
"childCategoryId": 2001,
"childCategoryDescription": "Gordijnstoffen",
"categoryLinkMaterial": {
"materialCode": "1234",
"materialId": 55407,
"materialDescription": "Fabric A",
"price": {
"totalPrice": 323.01,
"totalPriceBeforePromotions": 323.01,
"priceGroups": [
{
"code": "MATERIAL",
"description": "Material",
"displayOrder": 998,
"elements": [
{
"code": "1234",
"quantity": 8.92,
"description": "Fabric A",
"unit": "m",
"unitPriceBeforePromotion": 19.36,
"unitPrice": 19.36,
"totalPrice": 172.6912,
"totalPriceBeforePromotion": 172.6912,
"sbnid": 2,
"purchasePriceInfo": {
"unitPurchasePrice": 0.0,
"totalPurchasePrice": 0.0
}
}
]
},
{
"code": "WAGE",
"description": "Wage",
"displayOrder": 999,
"elements": [
{
"code": "C1234",
"quantity": 4.0,
"description": "C1234: Confection tape 8",
"unit": "bn",
"unitPriceBeforePromotion": 37.58,
"unitPrice": 37.58,
"totalPrice": 150.32,
"totalPriceBeforePromotion": 150.32,
"sbnid": 2,
"purchasePriceInfo": {
"unitPurchasePrice": 18.79,
"totalPurchasePrice": 75.16
}
}
]
}
],
"promotions": [],
"discountAmount": 0.0,
"totalPurchasePrice": 75.16
},
"properties": [
{
"id": 12842,
"interfaceKey": "Curtain.Fabric.Quantity",
"propertyId": 28,
"dataType": 4,
"description": "Aantal",
"value": "1",
"displayLabel": "Aantal",
"displayOrder": 1,
"controlType": 1,
"listValues": null,
"isVisible": true,
"exclude": false
},
{
"id": 12839,
"interfaceKey": "Curtain.Fabric.Distribution",
"propertyId": 16,
"dataType": 11,
"description": "Distribution",
"value": "S",
"displayLabel": "Distribution",
"displayOrder": 2,
"controlType": 2,
"listValues": [
{
"interfaceKey": "Curtain.Fabric.Distribution.Pair",
"description": "Stel",
"value": "S",
"remarks": null,
"imageUrl": null
},
{
"interfaceKey": "Curtain.Fabric.Distribution.Left",
"description": "Stuk links",
"value": "SL",
"remarks": null,
"imageUrl": null
},
{
"interfaceKey": "Curtain.Fabric.Distribution.Right",
"description": "Stuk rechts",
"value": "SR",
"remarks": null,
"imageUrl": null
}
],
"isVisible": true,
"exclude": false
},
{
"id": 12855,
"interfaceKey": "Curtain.Fabric.Tape",
"propertyId": 72,
"dataType": 11,
"description": "Bandsoort",
"value": "8",
"displayLabel": "Band",
"displayOrder": 3,
"controlType": 2,
"listValues": [
{
"interfaceKey": "Curtain.Fabric.Tape.6",
"description": "6 cm",
"value": "6",
"remarks": null,
"imageUrl": null
},
{
"interfaceKey": "Curtain.Fabric.Tape.8",
"description": "8 cm",
"value": "8",
"remarks": null,
"imageUrl": null
},
{
"interfaceKey": "Curtain.Fabric.Tape.10",
"description": "10 cm",
"value": "10",
"remarks": null,
"imageUrl": null
}
],
"isVisible": true,
"exclude": false
},
{
"id": 12827,
"interfaceKey": "Curtain.Fabric.Pleat",
"propertyId": 3,
"dataType": 11,
"description": "Plooi",
"value": "V",
"displayLabel": "Plooi",
"displayOrder": 11,
"controlType": 2,
"listValues": [
{
"interfaceKey": "Curtain.Fabric.Pleat.Flat",
"description": "Ongeplooid",
"value": "G",
"remarks": null,
"imageUrl": null
},
{
"interfaceKey": "Curtain.Fabric.Pleat.Single",
"description": "Enkele plooi",
"value": "E",
"remarks": null,
"imageUrl": null
},
{
"interfaceKey": "Curtain.Fabric.Pleat.Double",
"description": "Vlinderplooi",
"value": "V",
"remarks": null,
"imageUrl": null
}
],
"isVisible": true,
"exclude": false
}
],
"supplier": {
"id": 47,
"sbnid": 2,
"searchName": "SANDBOX",
"shortName": "SANDBOX",
"name": "Sandbox Supplier"
}
}
]
}
]
}
}The actual project response can contain more fields depending on the CPQ configuration, selected product model, price setup, language, and permissions.
Empty Response
204 No Content means no project data was returned for the request. Treat this as a “not available" result and do not continue to manufacturing without user review.
Error Response
400 Bad Request returns ProblemDetails.
{
"type": "https://sandbox.decoloop.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The projectId field is required.",
"instance": "/api/Gateway/Embedded/LoadProject"
}Send a Project to Manufacturing
Sends a configured project to manufacturing.
Endpoint: POST /api/Gateway/Embedded/SendProject
This endpoint should be called only after the user has explicitly confirmed the order. Sending a project changes the project status and makes configured polygons read-only.
Request
POST https://sandbox.decoloop.com/api/Gateway/Embedded/SendProject
Authorization: bearer ACCESS_TOKEN
Content-Type: application/json
Accept: application/json
{
"projectId": 69623,
"reference": "Order Ref 12345",
"setDeliveryAddress": false,
"customer": {
"companyName": "Sandbox Corp",
"lastName": "Doe",
"website": null,
"mainAddress": {
"name": "",
"street": "Main Street",
"streetNumber": "123",
"zip": "1234 AB",
"city": "Amsterdam",
"countryCode": "NL"
}
}
}Request Body
| Field | Type | Required | Description |
|---|---|---|---|
projectId | integer | Yes | ID of the project to send to manufacturing. |
customer | object or null | No | Customer data to attach to the project. If customer.id is 0, a new customer may be created. If an existing ID is supplied, the customer may be updated. |
reference | string or null | No | Project reference. When empty, the customer name may be used automatically. |
setDeliveryAddress | boolean | Yes | When true, the customer address is used as the delivery address. If customer.deliveryAddress is filled, that address is used. |
Customer Object
Common customer fields include:
| Field | Type | Description |
|---|---|---|
id | integer | Existing customer ID, or 0 for a new customer. |
code | string or null | Debtor code. Usually treated as read-only. |
deliveryCode | string or null | Delivery code. Usually treated as read-only. |
companyName | string or null | Company name, if applicable. |
firstName | string or null | Customer first name. |
middleName | string or null | Customer middle name. |
lastName | string or null | Customer last name. |
displayName | string or null | Display-friendly customer name. |
phone | string or null | Phone number. |
mobile | string or null | Mobile number. |
eMail | string or null | Email address. |
mainAddress | object or null | Main customer address. |
deliveryAddress | object or null | Delivery address. |
invoiceAddress | object or null | Invoice address. |
Address Object
Common address fields include:
| Field | Type | Description |
|---|---|---|
name | string or null | Name associated with the address. |
street | string or null | Street name. |
streetNumber | string or null | Street number. |
zip | string or null | Postal code. |
city | string or null | City. |
countryCode | string or null | ISO-style country code, for example NL or BE. |
phoneNumber | string or null | Phone number for the address. |
emailAddress | string or null | Email address for the address. |
contactName | string or null | Contact person. |
Successful Response
200 OK returns a boolean.
trueA response of true means the project was successfully sent to manufacturing.
A response of false means the project was not sent. The host application should keep the project in a pending state and show an actionable message to the user or support team.
Error Response
{
"type": "https://sandbox.decoloop.com/problems/project-send-failed",
"title": "Project could not be sent",
"status": 400,
"detail": "The project is incomplete and cannot be sent to manufacturing.",
"instance": "/api/Gateway/Embedded/SendProject"
}Browse Materials
Browses and filters materials available to the authenticated user/licensee.
Endpoint: POST /api/Gateway/Embedded/BrowseMaterials
The result is paginated and can be filtered by:
- Category
- Supplier
- Free-text search term
Request
POST https://sandbox.decoloop.com/api/Gateway/Embedded/BrowseMaterials
Authorization: bearer ACCESS_TOKEN
Content-Type: application/json
Accept: application/json
{
"categoryId": 2001,
"supplierId": 47,
"searchTerm": "blackout",
"pageNumber": 1,
"pageSize": 25
}Request Body
| Field | Type | Required | Description |
|---|---|---|---|
categoryId | integer or null | No | Filters materials by category. Omit or use null to include all categories. |
supplierId | integer or null | No | Filters materials by supplier. Omit or use null to include all suppliers. |
searchTerm | string or null | No | Case-insensitive free-text search against material name or description. |
pageNumber | integer | Yes | 1-based page number. Use 1 for the first page. |
pageSize | integer | Yes | Number of materials to return per page. Use a reasonable page size, for example 25 or 50. |
Successful Response
{
"items": [
{
"id": 292515,
"code": "5971",
"categoryId": 2001,
"eanCode": null,
"supplierCode": null,
"description": "Standard Blackout White",
"remarks": null,
"promotionPrice": null,
"salePrice": 24.95,
"purchasePrice": null,
"saleQuantity": 1,
"saleUnit": "m",
"leadTime": 10,
"imageUrl": null,
"supplierId": 47,
"isWage": false,
"color": "#FFFFFF",
"categoryDescription": "Gordijnstoffen",
"extraFieldValues": [],
"promotions": [],
"materialOptionValues": [],
"displayProperties": [
{
"description": "Color",
"value": "White",
"valueKind": 0
}
],
"attachments": [],
"materialGroups": []
},
{
"id": 292516,
"code": "5972",
"categoryId": 2001,
"eanCode": null,
"supplierCode": null,
"description": "Premium Blackout Grey",
"remarks": null,
"promotionPrice": null,
"salePrice": 29.95,
"purchasePrice": null,
"saleQuantity": 1,
"saleUnit": "m",
"leadTime": 10,
"imageUrl": null,
"supplierId": 47,
"isWage": false,
"color": "#888888",
"categoryDescription": "Gordijnstoffen",
"extraFieldValues": [],
"promotions": [],
"materialOptionValues": [],
"displayProperties": [
{
"description": "Color",
"value": "Grey",
"valueKind": 0
}
],
"attachments": [],
"materialGroups": []
}
],
"totalItemCount": 2
}Response Fields
| Field | Type | Description |
|---|---|---|
items | array | Materials for the requested page. |
totalItemCount | integer | Total number of materials matching the filters before paging. |
Important material fields:
| Field | Description |
|---|---|
id | Material ID to use with iframe materialId. IDs are scoped to the authenticated CPQ context. |
code | Material code used by the backend configuration. This can be an EAN code or supplier code. |
eanCode | Material EAN code, when available. |
supplierCode | Supplier-specific material code, when available. |
description | Localized material description. |
salePrice | Sale price, when available for the authenticated user. |
purchasePrice | Purchase price, when available and permitted. Do not expose this to end users unless intended. |
imageUrl | Relative or absolute image URL, depending on CPQ configuration. |
supplierId | Supplier ID. |
categoryId | Material category ID. |
categoryDescription | Localized category description. |
Material data is requestor-dependent. The same material ID or code can have different descriptions, prices, colors, or availability in another CPQ instance, licensee, supplier configuration, or language.
Get All Suppliers
Returns all suppliers available to the current licensee.
Endpoint: GET /api/Gateway/Embedded/GetAllSuppliers
Use this endpoint to build a supplier filter for material browsing.
Request
GET https://sandbox.decoloop.com/api/Gateway/Embedded/GetAllSuppliers
Authorization: bearer ACCESS_TOKEN
Accept: application/jsonSuccessful Response
[
{
"id": 1,
"sbnid": 100001,
"searchName": "supplier a",
"shortName": "Supplier A",
"name": "Supplier A"
},
{
"id": 2,
"sbnid": 100002,
"searchName": "supplier b",
"shortName": "Supplier B",
"name": "Supplier B"
},
{
"id": 3,
"sbnid": 100003,
"searchName": "supplier c",
"shortName": "Supplier C",
"name": "Supplier C"
},
{
"id": 4,
"sbnid": 100004,
"searchName": "supplier d",
"shortName": "Supplier D",
"name": "Supplier D"
}
]Supplier Fields
| Field | Type | Description |
|---|---|---|
id | integer | Supplier ID. Use this as supplierId in BrowseMaterials. |
sbnid | integer or null | Globally unique SBN ID, when available. |
searchName | string or null | Search-friendly supplier name. |
shortName | string or null | Short supplier name. |
name | string or null | Full supplier company name. |
Get Material Categories
Returns selectable material categories for the current licensee and language.
Endpoint: GET /api/Gateway/Embedded/GetMaterialCategories
Use this endpoint to build a category filter for material browsing.
Request
GET https://sandbox.decoloop.com/api/Gateway/Embedded/GetMaterialCategories
Authorization: bearer ACCESS_TOKEN
Accept: application/jsonSuccessful Response
[
{
"id": 2001,
"description": "Curtain fabrics",
"interfaceKey": "Curtain.Fabric"
}
]Category Fields
| Field | Type | Description |
|---|---|---|
id | integer | Category ID. Use this as categoryId in BrowseMaterials. |
description | string or null | Localized category description. |
interfaceKey | string or null | Integration key configured for the category. |
Optional Iframe URL Parameters
The iframe URL can be customized with optional parameters to control the initial state of Decoloop CPQ.
All iframe URLs should include:
token=ACCESS_TOKEN&isEmbedded=trueLoading an Existing Project
To open an existing project in the iframe, pass the project ID:
https://sandbox.decoloop.com?token=ACCESS_TOKEN&isEmbedded=true&projectId=69623This loads the saved project into Decoloop CPQ.
To retrieve the same project through the API, use:
POST /api/Gateway/Embedded/LoadProjectStart With a Material
To start a new project with a preselected material, use either materialId or materialCode.
When Decoloop CPQ starts with a material, it selects the first product from the menu for the first polygon.
Use POST /api/Gateway/Embedded/BrowseMaterials to find available materials for the authenticated user.
Material ID
https://sandbox.decoloop.com?token=ACCESS_TOKEN&isEmbedded=true&materialId=292515Material Code
https://sandbox.decoloop.com?token=ACCESS_TOKEN&isEmbedded=true&materialCode=5971If an invalid material ID or material code is passed, Decoloop CPQ shows an error message.
Copy Project
To copy an existing project, use the copy parameter together with projectId:
https://sandbox.decoloop.com?token=ACCESS_TOKEN&isEmbedded=true©=true&projectId=69623Copy Polygon
To copy a specific polygon from a project, use copyPolygonId together with projectId:
https://sandbox.decoloop.com?token=ACCESS_TOKEN&isEmbedded=true&projectId=69623©PolygonId=501Error Handling
Embedded API endpoints can return ProblemDetails for invalid requests.
Example:
{
"type": "https://sandbox.decoloop.com/problems/invalid-request",
"title": "Invalid request",
"status": 400,
"detail": "The pageSize field must be greater than zero.",
"instance": "/api/Gateway/Embedded/BrowseMaterials"
}Recommended handling:
| Status | Meaning | Recommended action |
|---|---|---|
200 | Request succeeded. | Process the response. |
204 | No content, possible for LoadProject. | Treat as no project returned. Ask the user to retry or contact support. |
400 | Invalid request or business validation failure. | Show a clear validation message and log the ProblemDetails.detail. |
401 | Missing or expired token. | Request a new token and retry once. |
403 | Authenticated but not allowed. | Do not retry automatically. Contact Decoloop or check permissions. |
5xx | Server error. | Show a generic error and retry later. Log correlation details if available. |
Security Recommendations
- Do not expose API credentials or API keys in browser code.
- Store tokens securely and avoid logging them.
- Always validate
event.originbefore trusting messages from the iframe. - Treat project IDs, material IDs, supplier IDs, and category IDs as scoped to the authenticated CPQ context.
- Do not cache material or supplier data globally across tenants, licensees, languages, or CPQ instances.
- Avoid exposing purchase prices to end users unless the integration explicitly requires it and the user is authorized.
- Require explicit user confirmation before calling
SendProject.
Next Steps
- Use the full API reference for detailed schema information.
- Contact Decoloop if you need access to additional embedded permissions, purchase price visibility, or custom customer/address fields.