API
Customer Addresses
<form class="address">
<input type="text" name="firstName">
<input type="text" name="lastName">
<input type="text" name="street">
<input type="text" name="city">
<input type="text" name="region">
<input type="text" name="zip">
<input type="text" name="country">
<input type="email" name="email">
<input type="tel" name="phone">
<input type="text" name="companyName">
<input type="text" name="companyId">
<input type="text" name="vatId">
</form>document.querySelector('form.address').addEventListener('submit', function(event){
event.preventDefault();
var button = this.querySelector('[type="submit"]');
button.disabled = true;
button.classList.add('is-loading');
api.user.address.add(new FormData(this))
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.address-error').textContent = error.message;
})
.finally(function(){
button.disabled = false;
button.classList.remove('is-loading');
});
});
document.querySelector('form.address').addEventListener('submit', function(event){
event.preventDefault();
var button = this.querySelector('[type="submit"]');
button.disabled = true;
button.classList.add('is-loading');
api.user.address.update(123456789, new FormData(this))
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.address-error').textContent = error.message;
})
.finally(function(){
button.disabled = false;
button.classList.remove('is-loading');
});
});
document.querySelector('a.delete-address').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.user.address.delete(123456789)
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.address-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});
api.user.address.get(123456789)
.then(function(response){
document.querySelector('[name="firstName"]').value = response.firstName;
document.querySelector('[name="lastName"]').value = response.lastName;
// ... fill remaining fields
})
.catch(function(error){
document.querySelector('.address-error').textContent = error.message;
});Newsletter (logged-in customer)
api.user.newsletter.unsubscribe
document.querySelector('a.customer-unsubscribe').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.user.newsletter.unsubscribe()
.then(function(){
window.location.reload();
})
.catch(function(error){
document.querySelector('.newsletter-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});Order
api.order.delete
document.querySelector('a.delete-order').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.order.delete()
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.order-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.order.voucher.add
document.querySelector('a.add-voucher').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.order.voucher.add('XYZ1234')
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.voucher-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.order.voucher.delete
document.querySelector('a.delete-voucher').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.order.voucher.delete('XYZ1234')
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.voucher-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.order.item.changeAmount
document.querySelector('a.remove-change-amount').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.order.item.changeAmount(/* item id*/ 123, 7 /* new amount (cannot be zero) */)
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.cart-error').textContent = error.message;
// Currently one of stock, unavailable, min_amount, max_amount, amount_multiple
// More can be added later - always fallback to error message
console.log(error.code);
// translated error message
console.log(error.message);
// item name (useful if rule was triggered by bundle part)
console.log(error.item.name);
// correct amount for current validation (may not be valid for next validation)
// zero for unavailable items
console.log(error.recommendedAmount);
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.order.item.remove
document.querySelector('a.remove-cart-item').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.order.item.delete(123)
.then(function(response){
window.location.reload();
})
.catch(function(error){
document.querySelector('.cart-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.order.status.lookup
Look up order status using the order code and email. This method is protected by proof-of-work and rate limiting.
document.querySelector('form.order-lookup').addEventListener('submit', function(event){
event.preventDefault();
var button = this.querySelector('[type="submit"]');
button.disabled = true;
button.classList.add('is-loading');
var orderCode = this.querySelector('[name="orderCode"]').value;
var email = this.querySelector('[name="email"]').value;
api.order.status.lookup(orderCode, email)
.then(function(response){
// redirect to order status page
window.location.href = response.url;
})
.catch(function(error){
document.querySelector('.order-lookup-error').textContent = error.message;
})
.finally(function(){
button.disabled = false;
button.classList.remove('is-loading');
});
});Products
api.product.compare.add
document.querySelector('a.compare-product').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.product.compare.add(123)
.then(function(response){
response.count // number of products in comparison
response.url // URL to the product comparison page
})
.catch(function(error){
document.querySelector('.compare-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.product.compare.remove
document.querySelector('a.compare-product').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.product.compare.remove(123)
.then(function(response){
response.count // number of products in comparison
response.url // URL to the product comparison page
})
.catch(function(error){
document.querySelector('.compare-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.product.compare.clear
document.querySelector('a.compare-product').addEventListener('click', function(event){
event.preventDefault();
var link = this;
link.classList.add('is-loading');
api.product.compare.remove()
.then(function(response){
})
.catch(function(error){
document.querySelector('.compare-error').textContent = error.message;
})
.finally(function(){
link.classList.remove('is-loading');
});
});api.log.feature.used
Feature usage logging is intended for tracking less frequently used features over a limited period -- for example, for A/B testing. The name can be arbitrary -- it is only used for debug reporting.
document.querySelector('a.compare-product').addEventListener('click', function(event){
event.preventDefault();
api.log.feature.used('product-added-to-compare');
});api.geo.suggest.location
Returns a list of locations (addresses, cities, regions, ...) for the given phrase. Optionally, results can be filtered by country ("cz") or biased by coordinates. If the exact location is not known, it should always be used at least with GeoIP coordinates.
api.geo.suggest.location('Drtinova 10', 'cz', 50.0843, 14.4075);Integrations
api.integration.call
Call a custom integration synchronously from the frontend and receive its JSON response. Useful for shop-specific behavior provided by an external integration — for example, live address validation against a vendor service, dynamic shipping pricing, or vendor-specific availability checks.
The integration must declare <action>frontend.call</action> in its manifest. The first argument is the integration's repository name; the second is an arbitrary payload object that the integration will receive verbatim. The Lambda also receives the request context (host, client IP, optional logged-in user id) so it can act on behalf of the visitor without trusting client-supplied identity.
Errors: a missing or non-frontend integration responds with HTTP 404; a Lambda-side error responds with HTTP 502. Cold starts can take several seconds — show a spinner.
document.querySelector('form.availability').addEventListener('submit', function(event){
event.preventDefault();
var button = this.querySelector('[type="submit"]');
button.disabled = true;
button.classList.add('is-loading');
var sku = this.querySelector('[name="sku"]').value;
api.integration.call('vendor-availability', { sku: sku })
.then(function(response){
// shape of `response` is whatever the integration returns
document.querySelector('.availability-result').textContent = response.inStock ? 'in stock' : 'out of stock';
})
.catch(function(error){
document.querySelector('.availability-error').textContent = error.message;
})
.finally(function(){
button.disabled = false;
button.classList.remove('is-loading');
});
});