Skip to main content

Power custom experiences on the Customer portal

Use Loop's custom JavaScript events, window variables and custom sections on customer portal to trigger personalized, customer-specific experiences when subscribers take actions in customer portal.

You can customize the customer portal experiences using any of the following Loop's capabilities:

Custom JS events

Customer portal fires custom JavaScript (JS) events whenever a subscriber takes an action, such as adding a product, applying a discount, or skipping an order. Store developers can listen for these events to trigger custom experiences, like sending a contextual email or updating a record in an external tool. Each event carries a payload identifying the exact customer and subscription ID behind the action, so you can scope those experiences to individual subscribers.

This article covers the events available, the payload each one returns, how to set up event listeners, and common use cases.

What's in the event payload

Every customer portal event returns a detail object containing two identifiers:

Property

Description

shopifyCustomerId

The Shopify Customer ID of the subscriber who triggered the action.

shopifySubscriptionId

The Shopify Subscription ID tied to the action.

These identifiers let you tie each event to a specific customer and subscription, instead of treating every action as a generic, store-wide ping.

Setting up event listeners

To use these events, set up event listeners in your code. A listener detects when a specific action occurs and runs the function you define.

Read the detail property from the event object to access the customer and subscription IDs:

window.addEventListener('loopAddAsOneTime', (event) => {   const { shopifyCustomerId, shopifySubscriptionId } = event.detail;    
// write code to trigger the custom experience here,
// now scoped to a specific customer/subscription });

Above example code listens to the loopAddAsOneTime event and pulls the customer and subscription IDs from the payload, so any experience you trigger is scoped to that specific subscriber.

Note: Existing event listeners continue to work without changes. The payload is an additive, non-breaking change, so listeners written without considering these additional details in payload continue to work as-is.

List of customer portal events

The customer portal events are grouped into three categories based on the type of action they represent.

Product-based events

  • loopAddAsOneTime: Triggered when a product is added as a one-time purchase.

  • loopAddAsSubscription: Triggered when a product is added on a subscription basis.

  • loopRemoveForOneTime: Triggered when a one-time product is removed.

  • loopRemoveFromSubscription: Triggered when a subscription product is removed.

  • loopProductSwap: Triggered when a product swap is made.

  • loopUpdateProductQuantity: Triggered when the quantity of a product is updated.

Subscription-based events

  • loopAddDiscount: Triggered when a discount is applied to a subscription.

  • loopRemoveDiscount: Triggered when a discount is removed.

  • loopChangeSubscriptionPlan: Triggered when a subscription plan is changed.

  • loopChangeDeliveryMethodRequested: Triggered when a change in delivery method is requested.

  • loopEditDeliveryDetails: Triggered when delivery details are edited.

  • loopUpdatePaymentMethodRequested: Triggered when an update to the payment method is requested.

  • loopCancelSubscription: Triggered when a subscription is canceled.

  • loopReactivateSubscription: Triggered when a subscription is reactivated.

  • loopPauseSubscription: Triggered when a subscription is paused.

  • loopResumeSubscription: Triggered when a subscription is resumed.

Order-based events

  • loopOrderNow: Triggered when an order is placed immediately.

  • loopSkipNextScheduledOrder: Triggered when the next scheduled order is skipped.

  • loopSkipFutureScheduledOrder: Triggered when future scheduled orders are skipped.

  • loopRescheduleNextOrder: Triggered when the next order is rescheduled.

  • loopDelayNextOrder: Triggered when the next order is delayed.

  • loopUnskipOrder: Triggered when a previously skipped order is reinstated.

Bundle-based events

  • loopBundleAddAsOneTime: Triggered when a bundle is added as a one-time purchase.

  • loopBundleAddAsSubscription: Triggered when a bundle is added on a subscription basis.

  • loopBundleRemoveForOneTime: Triggered when a one-time bundle is removed.

  • loopBundleRemoveFromSubscription: Triggered when a subscription bundle is removed.

  • loopBundleVariantChange: Triggered when a variant within a bundle is changed.

  • loopPresetBundleUpdateQuantity: Triggered when the quantity of a preset bundle is updated.

Use cases for customer portal events

As each event identifies the customer and subscription behind the action, you can build precise, contextual experiences on top of these events. Common use cases include:

  • Sending contextual communications: Trigger a Klaviyo event tied to the exact customer who took the action, rather than a generic action ping. For example, send a targeted email when a specific subscriber cancels.

  • Updating external records: Update a customer-specific record in your CRM or analytics tool the moment a subscriber takes an action.

  • Building subscription-level personalization: Trigger an experience for a single subscription rather than store-wide.

  • Tracking behavioral data: Monitor how subscribers interact with customer portal by tracking actions like product additions, updates, or removals.

  • And more!

Customer portal window variable

Loop's customer portal application offers a window variable making it easier for developers to access information related to customer, subscription, discounts and upgrade offers surfaced on the portal.

Use cases of window variables

The window.loopProps variable gives you access to read live data to trigger or personalize custom experiences in the portal.

Common use cases:

  • Read the customer's email, tags, or subscription status to conditionally render content for specific segments.

  • Show subscription details like the next billing date, delivery interval, or line items in a custom UI element.

  • Use the subscriber's active discounts or volume discount tier to display savings messaging.

Customer portal information available in window variables

The following information is available in the window.loopProps variable:

Customer

  • Shopify customer ID

  • Email

  • First name and last name

  • Customer tags

Subscription

  • Subscription ID and Shopify subscription ID

  • Status

  • Currency code

  • Completed orders count

  • Next billing date and next order date after skip

  • Upcoming order ID

  • Created date

  • Billing policy and delivery policy

  • Delivery method

  • Shipping address

  • Payment method

  • Line items

  • Total line item price

  • Delivery price

  • Available delivery intervals

  • Prepaid status and remaining prepaid orders count

Discounts

  • Applied subscription and line-item discounts

  • Discount type, title, and value

  • Total savings on the next order

Volume discounts

  • Active volume discount programs

  • Program type, channel, and eligibility

  • Discount tiers and thresholds

  • Currently eligible tier

Upgrade profiles

  • Available upgrade profiles for the subscriber

Customer portal window variable field reference

Field

Meaning

customer

customer.shopifyId

Shopify Customer ID

customer.email

Customer's email address

customer.first_name

Customer's first name

customer.last_name

Customer's last name

customer.tags

Customer tags in Shopify

subscription

subscription.id

Loop's internal subscription ID

subscription.shopifyId

Shopify Subscription ID

subscription.status

Subscription status (e.g. active)

subscription.hasBundle

Whether the subscription contains a bundle

subscription.totalLineItemDiscountedPrice

subscription.deliveryPrice

Delivery/shipping price

subscription.currencyCode

Currency code for the subscription

subscription.completedOrdersCount

Number of orders completed on this subscription

subscription.nextBillingDateEpoch

Next billing date (epoch timestamp)

subscription.nextOrderDateAfterSkipEpoch

Next order date if the upcoming order is skipped (epoch)

subscription.subscriptionDiscount

subscription.specificSubscriptionsSavingsNextOrder

subscription.upcomingOrderId

ID of the upcoming order

subscription.createdAt

Subscription creation date

subscription.billingPolicy.maxCycles

Maximum billing cycles

subscription.billingPolicy.minCycles

Minimum billing cycles

subscription.billingPolicy.interval

Billing interval unit (e.g. MONTH)

subscription.billingPolicy.intervalCount

Number of intervals per billing cycle

subscription.billingPolicy.anchorDay

subscription.billingPolicy.anchorMonth

subscription.billingPolicy.anchorType

subscription.billingPolicy.prepaidIntervalCount

subscription.deliveryPolicy.interval

Delivery interval unit (e.g. MONTH)

subscription.deliveryPolicy.intervalCount

Number of intervals per delivery cycle

subscription.deliveryPolicy.anchorDay

subscription.deliveryPolicy.anchorMonth

subscription.deliveryPolicy.anchorType

subscription.deliveryPolicy.prepaidIntervalCount

subscription.subscriptionAttributes

Custom attributes on the subscription

subscription.autoResume

subscription.discounts[].node.id

Discount ID

subscription.discounts[].node.type

Discount type (e.g. MANUAL)

subscription.discounts[].node.title

Discount title

subscription.discounts[].node.value.__typename

Discount value type, indicating whether the discount is a percentage (SubscriptionDiscountPercentageValue) or fixed amount (SubscriptionDiscountFixedAmountValue)

subscription.discounts[].node.value.percentage

Percentage value, present when the discount is a percentage type

subscription.discounts[].node.value.amount.amount

Fixed discount amount, present when the discount is a fixed amount type

subscription.discounts[].node.value.amount.currencyCode

Currency code for the fixed discount amount

subscription.discounts[].node.value.appliesOnEachItem

Whether the fixed amount applies to each item, present for fixed amount discounts

subscription.discounts[].node.targetType

What the discount applies to (e.g. LINE_ITEM)

subscription.discounts[].node.usageCount

Number of times the discount has been used

subscription.discounts[].node.entitledLines.all

Whether the discount applies to all line items

subscription.discounts[].node.entitledLines.lines.nodes[].id

IDs of the specific line items the discount applies to

subscription.discounts[].node.rejectionReason

subscription.discounts[].node.recurringCycleLimit

Number of cycles the discount recurs for

subscription.note

Note on the subscription

subscription.chargeOffset

subscription.totalLineItemPrice

Total price of line items before discounts

subscription.isPrepaid

Whether the subscription is prepaid

subscription.deliveryMethod

Delivery method (e.g. SHIPPING)

subscription.shippingAddress.id

Shipping address ID

subscription.shippingAddress.subscriptionContract_id

ID of the subscription contract the address belongs to

subscription.shippingAddress.first_name

Recipient's first name

subscription.shippingAddress.last_name

Recipient's last name

subscription.shippingAddress.name

Recipient's full name

subscription.shippingAddress.phone

Recipient's phone number

subscription.shippingAddress.company

Company name

subscription.shippingAddress.address1

Address line 1

subscription.shippingAddress.address2

Address line 2

subscription.shippingAddress.city

City

subscription.shippingAddress.zip

Postal/ZIP code

subscription.shippingAddress.country

Country

subscription.shippingAddress.province

Province/state

subscription.shippingAddress.province_code

Province/state code

subscription.shippingAddress.country_code

Country code

subscription.shippingAddress.createdAt

When the address was created

subscription.shippingAddress.updatedAt

When the address was last updated

subscription.availableDeliveryIntervals[].spName

Selling plan display name

subscription.availableDeliveryIntervals[].delivery_interval

Delivery interval unit

subscription.availableDeliveryIntervals[].delivery_interval_count

Number of delivery intervals

subscription.availableDeliveryIntervals[].billing_interval

Billing interval unit

subscription.availableDeliveryIntervals[].billing_interval_count

Number of billing intervals

subscription.availableDeliveryIntervals[].shopify_id

Shopify selling plan ID

subscription.totalSpecificSubscriptionSpentByCustomer

Total amount the customer has spent on this subscription

subscription.lastSuccessfulOrder.billing_date

Billing date of the last successful order (epoch)

subscription.lastSuccessfulOrder.chargeOffset

subscription.isPrepaidV2

subscription.isMarkedForCancellation

Whether the subscription is marked for cancellation

subscription.paymentErrorMessageDisplay

Payment error message shown to the subscriber

subscription.deliveryPriceCurrencyCode

Currency code for the delivery price

subscription.remainingPrepaidOrdersCount

Remaining prepaid orders

subscription.isPrepaidAutoRenewalEnabled

Whether prepaid auto-renewal is enabled

subscription.paymentMethod.payment_instrument.name

Name on the payment instrument

subscription.paymentMethod.payment_instrument.brand

Card brand

subscription.paymentMethod.payment_instrument.source

Payment source (e.g. credit_card)

subscription.paymentMethod.payment_instrument.__typename

Payment instrument type (e.g. CustomerCreditCard)

subscription.paymentMethod.payment_instrument.expiryYear

Card expiry year

subscription.paymentMethod.payment_instrument.lastDigits

Last digits of the card

subscription.paymentMethod.payment_instrument.expiryMonth

Card expiry month

subscription.paymentMethod.payment_instrument.billingAddress.zip

Billing address postal/ZIP code

subscription.paymentMethod.payment_instrument.billingAddress.city

Billing address city

subscription.paymentMethod.payment_instrument.billingAddress.country

Billing address country

subscription.paymentMethod.payment_instrument.billingAddress.address1

Billing address line 1

subscription.paymentMethod.payment_instrument.billingAddress.lastName

Billing address last name

subscription.paymentMethod.payment_instrument.billingAddress.province

Billing address province/state

subscription.paymentMethod.payment_instrument.billingAddress.firstName

Billing address first name

subscription.paymentMethod.payment_instrument.billingAddress.countryCode

Billing address country code

subscription.paymentMethod.payment_instrument.billingAddress.provinceCode

Billing address province/state code

subscription.paymentMethod.payment_instrument.virtualLastDigits

subscription.paymentMethod.is_revoked

Whether the payment method is revoked

subscription.paymentMethod.risk_level

Risk level of the payment method

subscription.paymentMethod.update_mail_sent_at

When a payment update email was sent

subscription.paymentMethod.expiring_30days_event_emitted_at

subscription.paymentMethod.expiring_15days_event_emitted_at

subscription.paymentMethod.expiring_3days_event_emitted_at

subscription.paymentMethod.paymentMethodLastUpdatedAt

When the payment method was last updated

subscription.lastBillingAttempt

Details of the last billing attempt

subscription.lineItems[].id

Line item ID

subscription.lineItems[].title

Product title

subscription.lineItems[].variantTitle

Variant title

subscription.lineItems[].price

Line item price

subscription.lineItems[].quantity

Quantity

subscription.lineItems[].variantId

Shopify variant ID

subscription.lineItems[].productId

Shopify product ID

subscription.lineItems[].discountAllocations[].amount.amount

Discount amount allocated to this line item

subscription.lineItems[].discountAllocations[].amount.currencyCode

Currency code for the allocated discount amount

subscription.lineItems[].discountAllocations[].discount.id

ID of the discount allocated to this line item

subscription.lineItems[].discountAllocations[].discount.type

Discount type (e.g. MANUAL)

subscription.lineItems[].discountAllocations[].discount.title

Discount title

subscription.lineItems[].discountAllocations[].discount.value.percentage

Percentage value, present when the discount is a percentage type

subscription.lineItems[].discountAllocations[].discount.value.amount.amount

Fixed discount amount, present when the discount is a fixed amount type

subscription.lineItems[].discountAllocations[].discount.value.amount.currencyCode

Currency code for the fixed discount amount

subscription.lineItems[].discountAllocations[].discount.value.appliesOnEachItem

Whether the fixed amount applies to each item, present for fixed amount discounts

subscription.lineItems[].discountAllocations[].discount.targetType

What the discount applies to (e.g. LINE_ITEM)

subscription.lineItems[].discountAllocations[].discount.usageCount

Number of times the discount has been used

subscription.lineItems[].discountAllocations[].discount.entitledLines.all

Whether the discount applies to all line items

subscription.lineItems[].discountAllocations[].discount.rejectionReason

subscription.lineItems[].discountAllocations[].discount.recurringCycleLimit

Number of cycles the discount recurs for

subscription.lineItems[].basePrice

Base price before discounts

subscription.lineItems[].isOneTimeAdded

Whether added as a one-time item

subscription.lineItems[].isOneTimeRemoved

Whether removed as a one-time item

subscription.lineItems[].discountedPrice

Price after discounts

subscription.lineItems[].bundleTransactionId

Bundle transaction ID, if part of a bundle

subscription.lineItems[].sellingPlanName

Selling plan name

subscription.lineItems[].sellingPlanId

Selling plan ID

subscription.lineItems[].sellingPlanGroupId

Selling plan group ID

subscription.lineItems[].sellingPlanGroupName

Selling plan group name

subscription.lineItems[].sku

SKU

subscription.lineItems[].requiresShipping

Whether the item requires shipping

subscription.lineItems[].originalDiscountedPrice

subscription.lineItems[].variantImage

Variant image URL

subscription.lineItems[].inventoryQuantity

Available inventory for the variant

subscription.lineItems[].productSku

Product SKU

subscription.lineItems[].productHandle

Product handle

subscription.lineItems[].canSwap

Whether the item can be swapped

subscription.lineItems[].customAttributes

Custom attributes on the line item

subscription.streak

subscription.flow_reward_text

allSubscriptions

allSubscriptions

upgradeProfiles

upgradeProfiles[].profileId

Upgrade profile ID

upgradeProfiles[].profileName

Upgrade profile name

upgradeProfiles[].priority

Priority of the upgrade profile

volumeDiscountData

volumeDiscountData.type

Volume discount type (e.g. ORDER_SIZE)

volumeDiscountData.programs[].programId

Program ID

volumeDiscountData.programs[].programNanoId

Program nano ID

volumeDiscountData.programs[].programName

Program name

volumeDiscountData.programs[].programLabel

Program label

volumeDiscountData.programs[].type

Program type (e.g. ORDER_SIZE)

volumeDiscountData.programs[].eligibilityBasedOn

What eligibility is based on (e.g. PRICE)

volumeDiscountData.programs[].priority

Program priority

volumeDiscountData.programs[].productEligibility

Which products are eligible (e.g. ALL)

volumeDiscountData.programs[].purchaseOption

Which purchase options are eligible (e.g. ALL)

volumeDiscountData.programs[].channel

Channel the program applies to (e.g. CUSTOMER_PORTAL)

volumeDiscountData.programs[].allTiers[].tierId

Tier ID

volumeDiscountData.programs[].allTiers[].tierNanoId

Tier nano ID

volumeDiscountData.programs[].allTiers[].minimumThreshold

Minimum threshold to qualify for the tier

volumeDiscountData.programs[].allTiers[].discountType

Discount type (e.g. PERCENTAGE)

volumeDiscountData.programs[].allTiers[].discountAmount

Discount amount

volumeDiscountData.programs[].allTiers[].discountLabel

Discount label

volumeDiscountData.programs[].allTiers[].isRecommended

Whether the tier is recommended

volumeDiscountData.programs[].currentlyEligibleTier.tierId

Tier ID of the tier the subscriber currently qualifies for

volumeDiscountData.programs[].currentlyEligibleTier.tierNanoId

Tier nano ID of the currently eligible tier

volumeDiscountData.programs[].currentlyEligibleTier.minimumThreshold

Minimum threshold of the currently eligible tier

volumeDiscountData.programs[].currentlyEligibleTier.discountType

Discount type of the currently eligible tier

volumeDiscountData.programs[].currentlyEligibleTier.discountAmount

Discount amount of the currently eligible tier

volumeDiscountData.programs[].currentlyEligibleTier.discountLabel

Discount label of the currently eligible tier

volumeDiscountData.programs[].currentlyEligibleTier.isRecommended

Whether the currently eligible tier is recommended

volumeDiscountData.programs[].eligibleVariantIds

Variant IDs eligible for the program

Custom section on the Customer portal

You can create and host multiple custom sections in the Customer portal with personalized texts powered by Loop's customer/subscription/store based variables.

Learn more about how to add and personalize a custom section.

FAQs

Do I need to change my existing event listeners to get the payload?

No, existing listeners continue to work without any changes. The payload is an additive, non-breaking change, so listeners written before this update keep functioning as-is. To use the new data, read the detail property from the event object.

Can I identify which subscriber triggered an event?

Yes, every customer portal event returns a detail object with shopifyCustomerId and shopifySubscriptionId, so you can tie each action to a specific customer and subscription.

Will the payload work with tools like Klaviyo or my CRM?

Yes, you can pass the shopifyCustomerId and shopifySubscriptionId from the payload into any tool you call inside your event listener, such as sending a customer-specific Klaviyo event or updating a CRM record.

Do these events fire for actions taken outside customer portal?

No, these events fire only for actions subscribers take within customer portal. Bundle builder and storefront actions use a separate set of events covered in Bundle JavaScript events & window variables for custom experience.

Need help?

No worries - we're here for you!

If you have any questions or need assistance, feel free to email us at [email protected] or chat with us using the support beacon at the bottom right of your screen.

Regards,

Loop subscriptions team 🙂

Did this answer your question?