Skip to main content
Client-side API

Learn how to integrate Discount Ninja into your theme or app using JavaScript

Bart Coppens avatar
Written by Bart Coppens
Updated over a week ago

Replaced by Developer Hub

Since April 2024, this content is considered obsolete.
Please refer to the documentation in the Developer Hub instead.

Events based integration

Discount Ninja publishes events that you can hook into. This allows you to add logic after specific events occur.

You can also publish events to inform Discount Ninja about changes on the web page.

Available integration points

Promotion code field

  • Add a discount code programmatically:


  • Remove a discount code programmatically:



Event: limoniapps:discountninja:promotions:loaded
Timing: this event is triggered by the app after promotions have been loaded and processed by the app

  • an array of objects representing the offers loaded by the app 


document.addEventListener("limoniapps:discountninja:promotions:loaded", function (event) { console.log('Promotions loaded',; [... your logic here ...] });

Object structure:

A promotion object contains:

  • Token: the token of the offer

  • TriggeredByTrigger: the trigger that triggered the offer, including its token (TriggeredByTrigger.Token), the Schedule, the Filter settings and an array of offer tokens in Offers.

  • Entitlement: describes what the offer includes

  • Tiers: describes further details of the offer per tier

  • Prerequisite: describes what needs to be in the cart for the offer to apply

  • Stacking: contains information about if/how the offer stacks and combines with other offers

  • Building blocks: the Notification, StickyBar, Cart and DrawerCart properties describe the configuration of the building blocks for the offer

Discounted product

Event: limoniapps:discountninja:product:discountapplied
Timing: this event is triggered by the app after the discounted price of a product on the product or collection pages is calculated

  •[0] an object representing the information available about the discounted price of the product


document.addEventListener("limoniapps:discountninja:product:discountapplied", function (event) { var discountedProductInfo =[0]; console.log('Product discounted', discountedProductInfo); [... your logic here ...] });

Object structure:

  • OriginalPrice: the original (non-discounted) price of the product

  • DiscountedPrice: the discounted price after applying Discount Ninja promotions

  • Amount: the amount of the discount

  • Percentage: the discount percentage

  • VariantId: the id of the variant of the product (only available on the product page, null on collection pages)

  • ProductHandle: the handle of the product

  • DiscountCode: the applied Shopify discount code or promotion code (defaults to the trigger token if not available)

  • OfferToken: the token of the Discount Ninja offer that was applied; you can use this token to look up further info on the offer using discountNinja.Offer.GetOfferByToken(...)

  • TriggerToken: the token of the Discount Ninja trigger that was applied; you can use this token to look up further info on the trigger using discountNinja.Trigger.GetTriggerByToken(...)


Event: limoniapps:discountninja:cart:updated
Timing: this event is triggered by the app after an update to the cart has been processed

  •[0] an object representing the cart with discounts applied


document.addEventListener("limoniapps:discountninja:cart:updated", function (event) { var cart =[0]; console.log('Cart updated', cart); [... your logic here ...] });

Object structure:

  • CartLevelDiscounts: an array of discounts applied at cart level

  • items: an array of line items that are present in the cart (linked to the Shopify cart using the Key) that includes information about the applied offer and trigger (OfferToken and TriggerToken) as well as information about the product (Product) and the line (Line) as well as the Properties applied to the line

  • total_discount: total amount of the discount

  • total_discounted_price: subtotal of the cart including discounts

  • total_original_price: subtotal of the cart excluding discounts

Prevent a checkout

Note 1: incorrect usage of this mechanism can result in a failure to display the discounted checkout.

Note 2: this mechanism is only compatible with Discount Ninja's Draft Order checkout mode .

To prevent a Discount Ninja checkout use the following code:

if (typeof discountNinja !== 'undefined') {
discountNinja.Checkout.Shopify.SkipProcessingCheckout = true;

To re-enable the Discount Ninja checkout use the following code:

if (typeof discountNinja !== 'undefined') {
discountNinja.Checkout.Shopify.SkipProcessingCheckout = false;

This mechanism should be used together with the checkout:initiate event in the following way:

  • prevent a checkout while another app or component executes logic before the checkout

  • re-enable the checkout when the app or component has executed its logic

  • trigger the checkout using the checkout:initiate event

Trigger a checkout

Event: limoniapps:discountninja:checkout:initiate
Timing: trigger this event to instruct Discount Ninja to initiate a checkout
Parameters: N/A


This approach is useful if you need a custom pipeline when clicking the checkout button.
For example, you may want to execute custom validation logic before allowing the checkout to proceed.

The following coding example shows how this can be achieved.
This code is meant as an illustration only; use it at your own risk in a production environment.


/// The markup below shows a custom button for illustration purposes
/// (i.e. not a button of type="submit" with name="checkout")
/// Note: we advise against using a custom checkout button
<button name="customcheckout" onclick="handleCustomCheckout()">
Check out


/// This function should be called by a custom checkout button 
/// such as the one described above
function handleCustomCheckout() {
if (canAdvanceToCheckout() === true) {
// Checks if Discount Ninja is available
if (discountNinja) {
// Check if a Discount Ninja offer is applied to the cart discountNinja.Checkout.Shopify.requiresAdvancedCheckout().then(function(requiresAdvancedCheckout) {
if (requiresAdvancedCheckout === true) {
// DN needs to handle the checkout
else {
else {
else {
... //Custom logic to explain why the user cannot advance

/// This optional function returns true if the user
/// can advance to the checkout
function canAdvanceToCheckout() {

/// This function instructs Discount Ninja
/// to advance to the checkout and apply offers
function advanceToCheckoutUsingDiscountNinja() {
document.dispatchEvent(new CustomEvent('limoniapps:discountninja:checkout:initiate'));

/// This function should redirect the user to the checkout
/// when a checkout with Discount Ninja is not required
function handleNonDiscountNinjaCheckout() {


Event: limoniapps:discountninja:stickybar:closed
Timing: this event is triggered by the app when the stickybar is closed
Parameters: N/A


document.addEventListener("limoniapps:discountninja:stickybar:closed", function (event) { console.log('Sticky bar closed'); [... your logic here ...] });


Event: limoniapps:discountninja:notification:closed
Timing: this event is triggered by the app when the notification is closed
Parameters: N/A


document.addEventListener("limoniapps:discountninja:notification:closed", function (event) { console.log('Notification closed'); [... your logic here ...] });

Event: limoniapps:discountninja:notification:minimized
Timing: this event is triggered by the app when the notification is minimized
Parameters: N/A


document.addEventListener("limoniapps:discountninja:notification:minimized", function (event) { console.log('Notification minimized'); [... your logic here ...] });

Event: limoniapps:discountninja:notification:maximized
Timing: this event is triggered by the app when the notification is maximized
Parameters: N/A


document.addEventListener("limoniapps:discountninja:notification:maximized", function (event) { console.log('Notification maximized'); [... your logic here ...] });

Drawer cart

Event: limoniapps:discountninja:drawercart:opened
Timing: trigger this event when the drawer cart has been opened or updated; this allows the app to ensure the correct discounted prices are displayed in the drawer cart
Parameters: N/A


document.dispatchEvent(new CustomEvent('limoniapps:discountninja:drawercart:opened'));

Event: limoniapps:discountninja:drawercart:refreshed
Timing: this event is triggered by the app after an update to the cart has been processed that should be reflected in the drawer cart; this event should be handled by the theme and should result in redrawing the drawer cart based on the cart provided

  •[0] an object representing the cart


document.addEventListener("limoniapps:discountninja:drawercart:refreshed", function (event) { var cart =[0]; console.log('Cart updated', cart); [... your logic here ...] });

Collection products

Event: limoniapps:discountninja:collection:productsadded
Timing: trigger this event when additional products are added dynamically to the collection page (e.g. infinite scroll) to ensure the correct discounted prices are displayed on the collection page
Parameters: N/A


document.dispatchEvent(new CustomEvent('limoniapps:discountninja:collection:productsadded')); 


Event: limoniapps:discountninja:variant:changed
Timing: trigger this event when a different variant is selected on the product pages; this event is only required if the standard mechanisms included in the app fail to detect when variants are changed automatically
Parameters: pass the variant id as a number

{ detail: { variant: add_the_variant_id_as_a_number } }


document.dispatchEvent(new CustomEvent('limoniapps:discountninja:variant:changed', { detail: { variant: add_the_variant_id_as_a_number } })); 

Trigger promotions programmatically

Promotions can be triggered programmatically via the trigger token.

Use the following command to trigger a promotion with the token ABCD.



Alternatively, you can create a button or link that can be used to trigger the promotion automatically once the element is clicked.

To do so:

  • Add the class limoniapps-discountninja-discountbutton to the element

  • Add the attribute data-limoniapps-discountninja-token to the element and set it to the token of the promotion

Bypass checkout

By default, Discount Ninja attaches to the click event of checkout buttons. When a promotion is active the app will create a discounted checkout and direct the visitor to this checkout.

There may be situations, specifically when integrating apps with Discount Ninja, where you need to bypass Discount Ninja's handling of the checkout. To do this, add the following class to the checkout button:


Avoid a checkout when custom fields have not been provided

When the checkout button is clicked, you may want to verify that specific, custom fields have a value before proceeding.

Typical use cases

  • Terms and conditions checkbox

Discount Ninja handles the first use case (Terms and conditions checkbox) out-of-the-box. It checks for known checkboxes that are related to terms and conditions. The app will not proceed to a discounted checkout until those checkboxes (if marked mandatory and visible) are checked. Note that the same logic should be present in your theme (or the app that handles this checkbox). Discount Ninja does not display an error message, it relies on the theme to do so.

To mark a checkbox as required before checkout, add the following CSS class to the input field:

  • Zapiet: shipping / store selection

Discount Ninja has built in integration with Zapiet.

It relies on a method named Zapiet.Widget.checkoutEnabled() which must evaluate to true.

  • Custom

For custom logic, you can subscribe a callback to the discountNinjaThirdPartyCheckoutReadyCallbacks object as follows:

discountNinjaThirdPartyCheckoutReadyCallbacks = [];
discountNinjaThirdPartyCheckoutReadyCallbacks.push(function() {
//Add your logic here
//Return true to indicate that the visitor can continue to the checkout
return true;

Currency conversion

Most currency conversion mechanisms are supported out-of-the-box by Discount Ninja. In case the currency conversion mechanism of your theme (or supporting app) is not automatically triggered, you can add the following logic to your theme.

In the JavaScript file of your theme, add a line to process the event that is dispatched by Discount Ninja when money fields should be converted.

Event: limoniapps:discountninja:convertmoneyfields
Timing: this event is triggered when all money fields have been updated by the app; this allows the theme to ensure the correct currency is applied

  • event. is the presentment currency known to Discount Ninja


document.addEventListener("limoniapps:discountninja:convertmoneyfields", function (event) { var presentmentCurrency =; YourThemesCurrencyConversionMethod(presentmentCurrency); });

Show/hide custom HTML

Discount Ninja includes building blocks that can display specific HTML content to help you promote offers.

If you need to show or hide custom HTML blocks, you can use the following classes:

  • limoniapps-discountninja-whenproductdiscounted-show and limoniapps-discountninja-whenproductdiscounted-hide: these classes can be used on product pages only; they hide or show the content of the element based on whether the selected variant is discounted by Discount Ninja or not

  • limoniapps-discountninja-whenactivepromotions-show and limoniapps-discountninja-whenactivepromotions-hide: hide or show the content of the element based on whether Discount Ninja active promotions are available for the visitor or not

  • limoniapps-discountninja-whenpromotionsincart-show and limoniapps-discountninja-whenpromotionsincart-hide: hide or show the content of the element based on whether Discount Ninja promotions have been applied to the cart or not

  • limoniapps-discountninja-whencartdiscounted-show and limoniapps-discountninja-whencartdscounted-hide: hide or show the content of the element based on whether the total amount of the cart is discounted by Discount Ninja promotions or not

Did this answer your question?