Docs  /  Integrations  /  JavaScript API (V3)

JavaScript API (V3)

Last updated: 28. October 2026

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:

  1. 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.
  2. window.Configento only exists once the configento.js script has run. It's loaded with defer, i.e. after the page has been built. So start your code on DOMContentLoaded or later – not directly in a script in the <head>.
  3. 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 mount from ctx, not from a variable of your own.
  • One renderer, several widgets. If there are two configurators on the page, the configurator calls mount twice. That's why update and destroy also 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:

  1. Embedding: instead of _configentoSettings and html5.js you use the V3 snippet with data-configento-* attributes. How is described in HTML Snippet Integration. The token is a different one; it comes from the HTML5 (V3) card.
  2. Your own code: from now on it addresses window.Configento – or keeps running via the compatibility script.
  3. 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

  1. Configento is not defined – your code runs before configento.js has loaded. Start it on DOMContentLoaded and wait for Configento.ready. Also check whether the page loads V3 at all (see above).
  2. Configento instance not ready when calling a method – the configurator is still loading. Only call methods inside Configento.ready.then(…).
  3. Configento.first() returns null – there's no element with data-configento on the page, or it was only inserted after loading.
  4. 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).
  5. storeAsset() reports CONFIG_MISSING – nothing can be saved in the admin preview. Test on your website or in the shop.
  6. Old theme code throws errors like Justselling_Configento_App_Wizard is not defined – the compatibility script is missing or comes after configento.js.

Next steps


← All articles Try free for 14 days