With the JavaScript API you connect the configurator to your own logic on the page: show the price in your purchase bar, transfer the selection into your own form, set values from outside, or save a configuration and reopen it later. The V3 frontend engine provides the window.Configento object for this – no matter whether it gets onto the page via the HTML snippet or a shop plugin. This guide uses ready-made examples to show how to solve typical tasks and how to move code written for the Legacy engine to V3. Every method with its signature, all events, data types and settings are listed in the JavaScript API reference (V3).
Where the API is available
window.Configento exists wherever the page loads the V3 engine:
| Integration | JavaScript API |
|---|---|
| HTML snippet (card HTML5 (V3)) | Always |
| Shopify | Always |
| PrestaShop | Always |
| Magento 2 | When the component is set to V3 or requires V3, see Frontend Engine: Legacy or V3 |
| Shopware 6 and the classic HTML5 snippet | No – both load Legacy |
The plugins use the API themselves, for example to put price and configuration into the cart. Your own code runs alongside – you don't need to switch anything off.
Getting started
<script>
window.addEventListener('DOMContentLoaded', function () {
Configento.ready.then(function () {
var configurator = Configento.first();
configurator.on('price', function (p) {
document.querySelector('#my-price').textContent = p.total.toFixed(2) + ' ' + p.currency;
});
configurator.on('change', function () {
console.log('Width:', configurator.getValue('width'));
});
});
});
</script>
Three rules that avoid almost all problems:
- Wait for
Configento.ready. Only then have all configurators on the page loaded. If you call a method before that, it reports that the configurator isn't ready yet. window.Configentoonly exists once theconfigento.jsscript has run. It's loaded withdefer, i.e. after the page has been built. So start your code onDOMContentLoadedor later – not directly in a script in the<head>.- The configurator looks for its places only once, on load: every element with
data-configento. Elements your code inserts later are not picked up.
You address characteristics by their internal name (ID), values by the Value field. The numeric IDs work as well.
Loading faster and reserving space
Three settings on the element with data-configento help while the page builds up:
| Attribute | Effect |
|---|---|
data-configento-min-height="640px" |
Reserves the height while the configurator loads – the page below doesn't jump when the first step appears. Afterwards the height adapts again. |
data-configento-min-height-keep |
Keeps the height afterwards as well |
data-configento-component-json='{…}' |
The component directly in the page, exactly as GET /api/v3/components/{id} returns it. Then there's no request on load. Only useful if your page keeps the published version up to date on the server side – after publishing, the page has to deliver the new version. |
In the JSON attribute data-configento-config they're called minHeight, minHeightKeep and component.
It's also worth adding a hint in the <head> that tells the browser to open the connection to the cloud early:
<link rel="preconnect" href="https://cloud.configento.app" crossorigin>
The configurator loads the component as soon as the script starts. Returning visitors don't load it again: if it hasn't changed since their last visit, the server just briefly confirms that.
Showing the price in your purchase bar
window.addEventListener('DOMContentLoaded', function () {
Configento.ready.then(function () {
var k = Configento.first();
var field = document.querySelector('#purchase-bar-price');
k.on('price', function (p) {
field.textContent = p.total.toLocaleString('en-GB', { style: 'currency', currency: p.currency });
});
});
});
price arrives once right after loading and then on every price change. Without code of your own, data-configento-price-mount="#purchase-bar-price" works too – then the configurator draws the price into your element itself.
Transferring the selection into your own form
For an inquiry via your own form, you validate the configuration with finalize() and write the result into hidden fields:
<form id="inquiry" action="/inquiry" method="post">
<input type="hidden" name="configuration">
<input type="hidden" name="summary">
<input type="hidden" name="sku">
<input type="hidden" name="price">
<button type="submit">Request a quote</button>
</form>
<script>
document.querySelector('#inquiry').addEventListener('submit', function (e) {
e.preventDefault();
var form = e.target;
var k = Configento.first();
k.finalize().then(function (result) {
if (!result.ok) return; // the errors are now shown on the characteristic
form.configuration.value = JSON.stringify(result.payload);
form.summary.value = k.getSummary({ format: 'text', separator: '\n' });
form.sku.value = k.getCombinedSku();
form.price.value = result.priceTotal;
form.submit();
});
});
</script>
finalize() shows missing required fields and errors from rules on the characteristic, just like the action button, but doesn't send anything itself. getCombinedSku() returns the combined article number from the selected values, e.g. CL-W-GR; it's also in result.payload as _combined_sku.
Presetting values
You address characteristics by their internal name, values by the Value field:
Configento.ready.then(function () {
var k = Configento.first();
k.selectValue('colour', 'anthracite'); // select, radio buttons, list with images
k.toggleValue('accessories', 'crank'); // checkbox, multi-selection
k.setText('width', '120'); // input field
k.setQuantity('screws', 'm6', 4); // multi-selection with quantities
});
Every call works like a click by your customer: rules, formulas and price are recalculated. With resetSelection('colour') you reset a characteristic.
From the address: this is how you take over ?colour=anthracite&width=120:
var params = new URLSearchParams(location.search);
if (params.has('width')) k.setText('width', params.get('width'));
if (params.has('colour')) k.selectValue('colour', params.get('colour'));
Variant changed in the shop: if formulas depend on product attributes such as width or weight, you pass on the new values with k.setProductAttributes({ width: '140' }). The selection stays, everything is recalculated. The initial values go into the JSON attribute as "productAttributes" when embedding.
Changing the language
If the language selection is switched on, showLanguageSelect() shows the flags again – for example for a "Change language" button:
<button type="button" onclick="Configento.first().showLanguageSelect()">Change language</button>
The current selection in the configurator is kept; after the new pick it continues in the new language. Without the language selection switched on, or for a component with only one language, nothing happens. You may also call the method before the configurator has loaded.
Controlling the wizard and groups
With the Tabs layout and in the Wizard, the configurator always shows one group. With these methods you build your own step display:
<ol id="steps"></ol>
<script>
Configento.ready.then(function () {
var k = Configento.first();
var list = document.querySelector('#steps');
function render() {
var active = k.getActiveGroup();
list.innerHTML = '';
k.getGroups().forEach(function (group) {
var item = document.createElement('li');
item.textContent = group.title;
if (active && group.id === active.id) item.className = 'active';
item.onclick = function () { k.setActiveGroup(group.id); };
list.appendChild(item);
});
}
render();
k.on('group', render); // after every step change
k.on('change', render); // rules can show and hide groups
});
</script>
getGroups() returns the groups shown with id, key, index, title and characteristics (the internal names of their characteristics), getActiveGroup() the current one. setActiveGroup() switches by ID or internal name and, going forward, checks every step on the way like Next; nextGroup() and prevGroup() correspond to Next and Back.
key is the group's internal name. You assign it in the editor in the group's settings under Internal name (ID), e.g. vehicle. Unlike the position, it stays the same when you reorder groups or a rule hides a group, and it reads better in code than a number. The element of the group shown also carries it as data-configento-group-key.
Your own check before the step changes
With setStepGuard() you run your own check before your customer moves on – for example a date of birth or a vehicle identification number. The check runs after the built-in required-field check, and only forward:
k.setStepGuard(function (step) {
if (step.from.key !== 'holder') return true;
if (isOfAge(k.getValue('date_of_birth'))) return true;
return [{ characteristic: 'date_of_birth', message: 'The holder must be of legal age.' }];
});
true lets the customer move on, false holds them without a message, a list holds them and shows each message on the named characteristic. The function may also return a promise, for example for a check over the network; until the answer arrives, Next and Back are locked. If it throws an error, the step changes anyway – your customer shouldn't get stuck.
Turning off the built-in navigation
If you build the step display yourself, hide the built-in one:
data-configento-wizard-nav |
Effect |
|---|---|
full (default) |
Tabs, progress bar, Back and Next |
buttons |
Only Back and Next |
none |
None of it – your code controls the steps |
The same setting is available in the editor under Settings → General → Group layout → Wizard navigation (V3); the attribute on the element takes precedence.
Showing the action button's result yourself
With Custom URL, the action button sends the configuration to your server. You receive your server's response through the action event – for example to show your own texts or to replace the form with a thank-you message after submitting:
<div id="thanks" hidden></div>
<p id="error" hidden></p>
<script>
window.addEventListener('DOMContentLoaded', function () {
Configento.ready.then(function () {
var k = Configento.first();
var thanks = document.querySelector('#thanks');
var error = document.querySelector('#error');
k.on('action', function (e) {
if (e.action === 'event') return; // "Hand over to the page" is handled by the listener further below
e.preventDefault(); // your own message instead of the button's
var text = e.body && e.body.message;
if (e.ok) {
k.element.hidden = true; // hide the form
error.hidden = true;
thanks.textContent = text || 'Thank you!';
thanks.hidden = false;
} else {
error.textContent = text || 'Submission failed (' + e.status + ').';
error.hidden = false;
}
});
});
});
</script>
Don't replace the content of the configurator element; hide it and show your message next to it. Without preventDefault() everything stays as before: the button shows its own message or redirects to the page under Redirect after action. All fields are listed in the JavaScript API reference.
Verifying the captcha with your own server endpoint
If the component contains a captcha, the customer's browser solves it in the background. If you send the data to your own server, your server must verify the solution – a check in the browser alone can be bypassed. There are two options for this.
Option 1: Action button with “Custom URL”. configento.app verifies the captcha and only then forwards the configuration to your address, signed with the secure key of your service. Your server only checks the signature – how is described under Action button: Custom URL. You evaluate your server's response with the action event (see above). Suitable when a fixed address is enough.
Option 2: Hand over to the page. Set the action button's action to Hand over to the page. The button checks the entries and hands the configuration over to your JavaScript; rules and the button's visibility still apply. Suitable when you have your own endpoint anyway or respond differently depending on the shop or language, for example with your own recipients.
<script>
window.addEventListener('DOMContentLoaded', function () {
Configento.ready.then(function () {
var k = Configento.first();
k.on('action', async function (e) {
if (e.action !== 'event') return;
var response = await fetch('/form/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(e.payload) // contains the captcha solution as "altcha"
});
var data = await response.json().catch(function () { return {}; });
return { ok: response.ok, message: data.message }; // the button shows the message
});
});
});
</script>
The button shows "Sending…" until your promise settles, and then your message – or you call e.preventDefault() and show everything yourself. The configurator resets the captcha by itself after every attempt. It also works without an action button: then you get the data from payload with k.finalize() and reset the captcha characteristic after every attempt with k.resetSelection('<internal name>').
Your server verifies the solution before it processes anything:
POST https://cloud.configento.app/api/altcha/verify
Content-Type: application/json
{"payload": "<value of altcha>"}
If configento.app responds with {"verified": true}, you process the data. With {"verified": false}, an error or no response, you reject the request, for example with HTTP 403.
Please note:
- Every solution is valid only once. So verify it only on the server and not beforehand in the browser.
- The verification address accepts at most 60 requests per minute per IP address. All verifications from your server share this limit.
- For option 2, choose the action Hand over to the page, not Custom URL – otherwise configento.app also sends the data to the address entered.
Saving and reopening a configuration
storeAsset() saves the current configuration and returns { id, url }. With the id you build a deep link: the address of the product page with ?dl= and the id. When someone opens this address, V3 restores the configuration in the first configurator on the page by itself.
k.storeAsset().then(function (saved) {
var link = new URL(location.href);
link.searchParams.set('dl', saved.id);
navigator.clipboard.writeText(link.toString());
}).catch(function (error) {
if (error instanceof Configento.CloudError && error.code === 'NO_SELECTIONS') {
alert('Please select something first.');
}
});
Restoring it yourself – for example in a specific one of several configurators:
Configento.loadAsset(id, { instance: k }).then(function (data) {
k.restoreFromAssetJson(data);
});
Nothing can be saved in the admin preview (CONFIG_MISSING); test on your website or in the shop. All error codes are listed in the reference.
Your own window for image selection
Some characteristics open a window, for example a list with images in the large view. By default V3 draws this window itself. If it should look like your shop's windows, you pass an adapter:
Configento.setModalAdapter({
openOverlay: function (args) {
// args.title, args.container (filled by the configurator), args.onClose
var modal = myShop.openModal(args.title, args.container, args.onClose);
return { close: function () { modal.close(); } };
}
});
Attach args.container to your window and call args.onClose() when your customer closes it. The configurator calls close() when it wants to close the window itself. Set the adapter as soon as window.Configento exists and before your customer opens a window. The Magento plugin already includes an adapter for the Magento windows.
Building your own input
If none of the characteristic types fits your input – say a licence plate, a colour picker on a wheel or a sketch to click on – you draw the characteristic yourself. For this, the configurator gives you an empty container at the characteristic's position and no longer touches it. You set values via the methods you receive; rules, price, required fields, wizard and order keep working as usual.
Example: a licence plate made of three parts. The component has three input fields – town, letters, digits – with their own required fields and validations. In the editor, all three get the entry kennzeichen in the CSS class field. Your script shows them as one plate:
(window.ConfigentoRenderers ||= {})['kennzeichen'] = {
combine: true, // all three characteristics become one widget
mount: function (el, ctx) {
var plate = document.createElement('div');
plate.className = 'kennzeichen';
ctx.characteristics.forEach(function (part) {
var field = document.createElement('input');
field.setAttribute('aria-label', part.title);
field.value = part.selection;
field.addEventListener('input', function () {
ctx.setText(field.value.toUpperCase(), part.key);
});
plate.appendChild(field);
});
el.appendChild(plate);
},
update: function (ctx, el) {
// Take over values from outside, e.g. after opening a saved configuration
el.querySelectorAll('input').forEach(function (field, i) {
var value = ctx.characteristics[i].selection;
if (field !== document.activeElement && field.value !== value) field.value = value;
});
}
};
Above the plate the configurator shows the title of the first part – so give it a title such as "Licence plate" – and below it the error messages of all three. You style the look with CSS for .kennzeichen.
A few rules for your own renderings:
- The value belongs to the configurator. In the wizard it tears down and rebuilds your container on every step change. So read the state in
mountfromctx, not from a variable of your own. - One renderer, several widgets. If there are two configurators on the page, the configurator calls
mounttwice. That's whyupdateanddestroyalso receive the container. - Nothing breaks without your script. In the admin preview, or if your script is missing, the configurator shows the built-in input fields.
- Only the look? Then CSS is enough, see Customising the design (CSS).
All details are in the reference.
Switching from Legacy
The Legacy engine had its own, older API: settings in the _configentoSettings object, the CONFIGENTO_PRODUCT object, global objects such as Justselling_Configento_App_Api and jQuery events such as configentoUpdateOptionsInDomAfter. When you switch to V3, three things change:
- Embedding: instead of
_configentoSettingsandhtml5.jsyou use the V3 snippet withdata-configento-*attributes. How is described in HTML Snippet Integration. The token is a different one; it comes from the HTML5 (V3) card. - Your own code: from now on it addresses
window.Configento– or keeps running via the compatibility script. - Features: what only Legacy can do is listed in Frontend Engine: Legacy or V3.
Settings
Legacy (_configentoSettings) |
V3 |
|---|---|
_template |
data-component-id |
_customer |
data-configento-customer |
_token |
data-configento-token – new token from the HTML5 (V3) card |
_cloudbase |
data-configento-cloud-base-url |
_taxrate |
data-configento-tax-rate |
_currencyCode |
data-configento-currency |
_cssPathCombinedImage or .configento-combinedImage |
data-configento-combined-image-mount |
Element #configento-price |
data-configento-price-mount |
Element #configurator1 |
An element with data-configento |
_loadjQuery, _loadChart, _loadDropzone |
Dropped – V3 brings everything along and doesn't need jQuery |
You no longer need _debug, _development, _cloudport and _productOptionId.
Option 1: Compatibility script
Shop themes in particular have accumulated code over the years that accesses the old objects. To keep it running under V3, you additionally load configento-compat.js before configento.js:
<script src="https://cloud.configento.app/configento-v3/configento-compat.js" defer></script>
<script src="https://cloud.configento.app/configento-v3/configento.js" defer></script>
The Magento plugin loads it by itself with V3. The script takes over:
| Legacy | What the script makes of it |
|---|---|
Events configentoUpdateOptionsInDomBefore/…After and configentoUpdateHeaderInDomBefore/…After |
Fired on document – reachable with jQuery .on() and with addEventListener. …Before comes on every change, …After as soon as the change is displayed, after every step change and once after the first display. The markup differs from Legacy, though: code that looks for elements such as #configurator1-… won't find them. |
CONFIGENTO_PRODUCT.setPrice(price) |
Called with the total price on every price change – provided your page defines CONFIGENTO_PRODUCT |
Element #configurator1 |
Becomes the place for the configurator; the component ID comes from CONFIGENTO_PRODUCT.getTemplateId() |
Justselling_Configento_App_Helper.getConfigurator(), getBackendUrl() |
Return the V3 configurator and the cloud address respectively |
Justselling_Configento_App_Webshop.finalizeAction(done) |
Validates with finalize(), writes the configuration into the cart's hidden form fields and then calls done |
Justselling_Configento_App_CloudAsset.storeCloudAsset(…) |
Saves with storeAsset(). The callback only receives the ID, not folder and file name. |
Justselling_Configento_App_Wizard.validateForm() and the other validation functions |
Always return true – validation happens on completion |
Justselling_Configento_App_Wizard.setActivateGroup(number) |
Switches to the group at this position, without a check |
setDisableGroup, setEnableGroup, updateWizard |
Do nothing |
If your code has already defined one of these objects itself, your version is kept.
What the script doesn't take over: it doesn't read _configentoSettings and doesn't create CONFIGENTO_PRODUCT – on a plain HTML page, html5.js used to do that. So there you need the data-configento-* attributes in any case. Also not taken over are Justselling_Configento_App_Api, Justselling_Configento_App_Summary and all other events. For those, see the counterparts below.
Option 2: Rewriting for the new API
In the long run, code using window.Configento is clearer: it receives content with the events, throws understandable errors and doesn't depend on element IDs. This is how old and new calls correspond – everything new goes in Configento.ready.then(…) on a configurator k = Configento.first():
| Legacy | V3 |
|---|---|
Justselling_Configento_App_Helper.getConfigurator() |
Configento.first() or Configento.forElement(…) |
Justselling_Configento_App_Api.getOptionValue(characteristic) |
k.getValue('characteristic') |
Justselling_Configento_App_Api.setOptionValue(characteristic, value), setOptionValues([…]) |
Depending on the data type k.selectValue(), k.toggleValue(), k.setText() or k.setQuantity() |
Justselling_Configento_App_Api.setEventHook(…) |
k.on('change', …) – likewise price, finalize, error, group and action |
setEventHook('configentoValidateGroupBefore', fn) (prevent the step) |
k.setStepGuard(fn) |
getConfigurator().getActiveGroup() |
k.getActiveGroup() |
Justselling_Configento_App_Wizard.setActivateGroup(number) |
k.setActiveGroup(id or internal name) |
Event configentoSetActiveGroupAfter |
k.on('group', …) |
CONFIGENTO_PRODUCT.setPrice(price) |
k.on('price', function (p) { … p.total … }) – or data-configento-price-mount |
Justselling_Configento_App_Summary.getSummaryAsText() |
k.getSummary() |
Justselling_Configento_App_Wizard.validateForm() |
k.finalize() → ok |
Justselling_Configento_App_Webshop.finalizeAction(done) |
k.finalize().then(…) |
Justselling_Configento_App_CloudAsset.storeCloudAsset(…) |
k.storeAsset() → { id, url } |
Field #configurator1-header or generateHeaderValue() |
k.getHeaderValue() (Base64, as before) or k.getAssetPayload() (JSON) |
Events configentoUpdateOptionsInDomAfter, configentoUpdateHeaderInDomAfter, configentoApiSetOptionValueAfter |
k.on('change', …) |
Rebuilding the configurator's markup in configentoUpdateOptionsInDomAfter |
Your own rendering with Configento.registerRenderer(), see Building your own input – or CSS, see Customising the design (CSS) |
Event configentoRenderSummaryAfter |
k.on('change', …) and k.getSummary() inside it |
Event configentoFinalizeActionAfter |
k.on('finalize', …) |
Events configentoRenderAfter, configentoLoadTemplateAfter |
Configento.ready |
| Events for charts, preview images and file uploads | No counterpart |
?dl=… and ?tb=1 in the address |
Unchanged |
Preselecting characteristics via the address, e.g. ?width=120 |
Not built in – a few lines, see Presetting values |
How to preselect characteristics via the address is shown in Presetting values.
Difference in events: the Legacy events only triggered jQuery handlers and mostly delivered no data. The V3 events pass the new selection or the new price directly – you no longer need to read anything from the DOM.
Troubleshooting
Configento is not defined– your code runs beforeconfigento.jshas loaded. Start it onDOMContentLoadedand wait forConfigento.ready. Also check whether the page loads V3 at all (see above).Configento instance not readywhen calling a method – the configurator is still loading. Only call methods insideConfigento.ready.then(…).Configento.first()returnsnull– there's no element withdata-configentoon the page, or it was only inserted after loading.- A call like
selectValue()throws an error – the internal name or the value is wrong, or the data type doesn't fit the method. Upper and lower case matter; you'll find the name in the editor under Internal name (ID). storeAsset()reportsCONFIG_MISSING– nothing can be saved in the admin preview. Test on your website or in the shop.- Old theme code throws errors like
Justselling_Configento_App_Wizard is not defined– the compatibility script is missing or comes afterconfigento.js.