🛒 AutoCart Docs

Events API

Reference for the AutoCart DOM events dispatched on document when cart gifts are added, removed, or swapped.

Overview

AutoCart's standard and Progressive storefront engines dispatch the same CustomEvents on document. These events let your theme or custom code react to gift changes without polling the cart API.

Enable them using the setting for the engine you use:

Storefront engineEvents opt-in
Standard Theme ExtensionSet onAutocartUpdateCallback: true in Custom Settings.
Progressive Theme ExtensionSelect Callback Function as the Cart refresh method.

Events are gated behind the corresponding callback option. The standard and Progressive engines use the same event names and payload contract, so one listener can handle both. When callback mode is disabled, that engine does not dispatch events.

Events Reference

Event NameWhen it FiresCancellableevent.detail Type
autocart:readyOnce, after the first rule evaluation completes on page loadNoAutoCartResult
autocart:beforeBefore cart mutations are executed (planned changes)NoAutoCartResult
autocart:updatedAfter evaluation and any mutations complete (debounced 100ms)NoAutoCartResult
autocart:errorWhen a cart mutation failsNo{ error: unknown, result?: AutoCartResult }

All events bubble, so you can listen on document or any ancestor element.

Result Object Shape

Every event carries an AutoCartResult object in event.detail:

{
  type: "swap",           // "add" | "remove" | "swap" | "noop"
  cartToken: "c1-abc123",
  itemCount: 4,           // resulting cart item count
  cart: { ... },          // full /cart.js snapshot
  changes: [
    {
      action: "removed",
      reason: "no_longer_qualifying",
      rule: {
        id: "BOGO_SUMMER",
        title: "BOGO_SUMMER",
        scope: "cart"
      },
      variantId: 44012345678,
      productId: 8012345678,
      handle: "summer-tote",
      title: "Summer Tote Bag",
      quantity: 1,
      properties: { _autocart: true, _title: "BOGO_SUMMER" }
    },
    {
      action: "added",
      reason: "qualified",
      rule: {
        id: "FREE_SUNGLASSES",
        title: "FREE_SUNGLASSES",
        scope: "cart"
      },
      variantId: 44098765432,
      productId: 8098765432,
      handle: "free-sunglasses",
      title: "Classic Sunglasses",
      quantity: 1,
      properties: { _autocart: true, _title: "FREE_SUNGLASSES" }
    }
  ]
}

type Values

ValueMeaning
"add"Only additions in this evaluation
"remove"Only removals in this evaluation
"swap"Both additions and removals (e.g., gift replaced)
"noop"Cart evaluated but no gift changes were needed

For Progressive automations, rule.scope identifies the strategy as "tiered" or "cyclic". Standard automation events retain their normal storefront, checkout, global, or remove scope.

Change Entry

Each entry in the changes array describes one cart line that was added or removed:

FieldTypeDescription
action"added" | "removed"Whether the line was added or removed
reasonstringWhy the change happened (see below)
ruleobject{ id, title, scope } of the rule that triggered the change
variantIdnumberShopify variant ID
productIdnumberShopify product ID (0 if unknown)
handlestringProduct handle
titlestringProduct title
quantitynumberLine quantity
propertiesobjectLine item properties

Reason Codes

ReasonActionDescription
qualifiedaddedRule conditions now met, gift added
no_longer_qualifyingremovedRule conditions no longer met
promotion_endedremovedRule's scheduled end date has passed
outdatedremovedRule or variant no longer exists in active rules
rule_triggeredremovedA scope="remove" rule actively removed the item
manualadded/removedCustomer selected or changed gifts in the picker dialog
unknownadded/removedA maintenance operation could not be attributed to a more specific reason

Progressive removals use outdated when the saved rule is no longer active, promotion_ended when its schedule is inactive, and no_longer_qualifying when the current cart no longer earns the gift. Progressive additions use qualified.

Usage Examples

Listen for Gift Changes

document.addEventListener('autocart:updated', (event) => {
  const { type, changes, cart } = event.detail;
 
  if (type === 'noop') return;
 
  console.log(`AutoCart ${type}: ${changes.length} change(s)`);
 
  for (const change of changes) {
    console.log(`${change.action} ${change.title} (${change.reason})`);
  }
 
  // Refresh your mini-cart with the updated cart data
  updateMiniCart(cart);
});

Lock UI During Mutations

document.addEventListener('autocart:before', () => {
  document.querySelector('.cart-drawer').classList.add('loading');
});
 
document.addEventListener('autocart:updated', () => {
  document.querySelector('.cart-drawer').classList.remove('loading');
});

Track Removed Gifts

document.addEventListener('autocart:updated', (event) => {
  const removed = event.detail.changes.filter(c => c.action === 'removed');
 
  for (const gift of removed) {
    // The removed gift data is only available here —
    // once removed, it can't be read back from /cart.js
    console.log(`Gift removed: ${gift.title} (reason: ${gift.reason})`);
  }
});

Wait for AutoCart Ready

document.addEventListener('autocart:ready', (event) => {
  console.log('AutoCart initialized', event.detail);
  // Safe to read initial gift state
});

Debouncing

When multiple cart changes happen in rapid succession (e.g., customer adds 3 items quickly), each storefront engine coalesces its own autocart:updated events with a 100ms debounce window. Only the final evaluation result from that engine is dispatched.

autocart:before is not debounced — it fires immediately before each mutation so your UI can show a loading state right away.

The debounce only affects autocart:updated. All other events (autocart:before, autocart:error, autocart:ready) fire immediately.

Using Both Storefront Engines

The standard and Progressive engines intentionally publish the same event names. If both are enabled, a document listener receives events from both. Inspect change.rule.scope or Progressive properties such as _autocart_prog, _ac_tier, and _ac_cyclic when your integration needs to distinguish the source.

On this page