> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://stoqapp.crisp.help/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# STOQ JavaScript API documentation

STOQ provides a powerful JavaScript API that gives you complete control over how back-in-stock and preorder features appear on your storefront. Perfect for developers who want to build custom experiences that match their unique brand and user flows. Access everything through the `window._RestockRocket` object when the app loads.

For complete documentation, detailed parameters, request/response examples, and interactive testing, visit https://docs.stoqapp.com/frontend/docs/javascript-api/ **and** https://docs.stoqapp.com/frontend/docs/custom-events/

**What you can build**
* Custom 'Notify me' buttons styled to match your brand
* Custom 'Preorder' widget for the ideal customer experiences
* Inline signup forms embedded anywhere
* Dynamic preorder badges and information
* Multi-variant selection experiences
* Analytics tracking with custom events

---

## Checking if STOQ JS API is available

Listen for the `stoq:loaded` event before using the API:

```javascript
window.addEventListener('stoq:loaded', (event) => {
  const { pageType, enabled, settings } = event.detail;

  if (enabled) {
    console.log('STOQ is ready on', pageType);
    // Safe to use window._RestockRocket methods
  }
});
```

**Page types:** `'product'`, `'collection'`, `'index'`, `'search'`, `'page'`

---

## Available Methods


| Method | Product Page | Collection/Home/Other Pages |
| ---- |
| openModal | ✅ | ✅ |
| openInlineForm | ✅ | ❌ |
| removeInlineForm | ✅ | ❌ |
| renderButtonForVariant | ✅ | ❌ |
| getSellingPlan + preorder methods | ✅ | ✅ |

---



## 1. openModal

Opens the notification modal for a specific product variant. This is your go-to method for triggering STOQ's signup experience from anywhere on your site - custom buttons, quick-add features, collection grids, you name it. The modal handles all the heavy lifting: form validation, duplicate detection, and submission.

```javascript
window._RestockRocket.openModal(productData, variantId, customerData)
```

#### Parameters:
* `productData` (Object, required):
```javascript
 {
    id: 123456789,              // Shopify product ID
    title: "Cool T-Shirt",
    handle: "cool-t-shirt",
    variants: [...]             // Product variants array
  }
```
* `variantId` (String, required): Variant ID to show modal for
* `customerData` (Object, optional): Pre-fill customer info
```javascript
 {
    email: "customer@example.com",
    phone: "+1234567890",
    name: "Jane Doe"
  }
```

#### Examples:

**On Collection Page:**
```javascript
const addToCartBtn = document.getElementById('quick-add-btn');
addToCartBtn.addEventListener('click', () => {
  const productData = {
    id: 1111,
    variants: [{ id: 2222, available: false }]
  };
  const variantId = '41XX';

  window._RestockRocket.openModal(productData, variantId);
});
```

**On Product Page (Simple):**
```javascript
const notifyMeBtn = document.getElementById('notify-me-btn');
notifyMeBtn.addEventListener('click', () => {
  // Opens modal for current product automatically
  window._RestockRocket.openModal();
});
```

**With Liquid:**
```liquid
<button onclick="window._RestockRocket.openModal({{ product | json }}, '{{ product.selected_or_first_available_variant.id }}')">
  Notify Me
</button>
```



## 2. openInlineForm

Embed the signup form directly into your page layout instead of showing it in a modal. Perfect for creating seamless, native-feeling experiences where you want the form to feel like part of your page rather than a popup. You control exactly where it appears and how it integrates with your design.

```javascript
window._RestockRocket.openInlineForm(productData, variantId, customerData, inlineFormContainer, inlineFormContainerInsertType)
```

#### Parameters:
* Same as `openModal` plus:
* `inlineFormContainer` (String, optional): CSS selector for container
* `inlineFormContainerInsertType` (String, optional): Where to insert - `'beforebegin'`, `'afterbegin'`, `'beforeend'` (default), `'afterend'`

#### Example:

```html
<div id="custom-notify-form"></div>

<script>
const addToCartBtn = document.getElementById('quick-add-btn');
addToCartBtn.addEventListener('click', () => {
  const productData = { id: 1111, variants: [{ id: 2222, available: false }] };
  const variantId = '41XX';

  window._RestockRocket.openInlineForm(
    productData,
    variantId,
    null, // customerData (optional)
    '#custom-notify-form', // Container selector
    'afterend' // Insert position
  );
});
</script>
```



## 3. removeInlineForm

Removes the inline form from the page.

```javascript
window._RestockRocket.removeInlineForm()
```

**Example:**
```javascript
// Remove form when variant changes or form is submitted
window._RestockRocket.removeInlineForm();
```



## 4. renderButtonForVariant

Automatically render the appropriate button (notify-me or preorder) for a specific variant. This method is smart - it checks inventory levels and preorder settings to show the right call-to-action. Great for themes with custom variant selectors where you want the button to update dynamically as customers choose options.

```javascript
window._RestockRocket.renderButtonForVariant(variantId, targetElement)
```

#### Parameters:
* `variantId` (String, required): ID of the variant
* `targetElement` (String, optional): CSS selector for container

#### Example:

```javascript
// Update button when variant selector changes
const variantSelector = document.getElementById('variant-selector');
variantSelector.addEventListener('change', (event) => {
  const selectedVariantId = event.target.value;
  window._RestockRocket.renderButtonForVariant(selectedVariantId);
});
```



## 5. Preorder Methods

STOQ exposes a comprehensive set of methods for building custom preorder experiences. Whether you're displaying availability counts, shipping dates, or building complex multi-payment-option flows, these methods give you full control.

### Core Preorder Methods

| Method | Description | Returns |
| ---- |
| `getSellingPlan(variantId)` | Get preorder config for a variant | Object or null |
| `getPreorderAvailable(variantId)` | Get available preorder quantity | Number |
| `getPreorderSold(variantId)` | Get number of preorders sold | Number |
| `getPreorderTotal(variantId)` | Get total preorder limit | Number |
| `getLineItemProperties(sellingPlan, variantId)` | Get properties for cart line items | Object |

### getSellingPlan

Get detailed preorder information for any variant. This gives you access to all the preorder configuration - button text, colors, shipping dates, limits, payment options, and more.

```javascript
const sellingPlan = window._RestockRocket.getSellingPlan(variantId)
```

**Returns:** Object with selling plan data or `null` if no preorder is set up for that variant.

#### Basic Example:

```javascript
const plan = window._RestockRocket.getSellingPlan("123456789");

if (plan) {
  console.log("Preorder available:", plan.name);
  console.log("Button text:", plan.preorder_button_text);
  console.log("Shipping date:", plan.delivery_exact_time);
  console.log("Payment options:", plan.payment_options);
} else {
  console.log("No preorder available");
}
```

**Key Selling Plan Properties:**
```javascript
{
  // IDs & Basic Info
  "shopify_selling_plan_group_id": 123456789,
  "shopify_selling_plan_id": 987654321,
  "enabled": true,
  "variant_ids": [11111, 22222, 33333],
  "name": "Preorder - Ships March 2024",

  // Button Styling
  "preorder_button_text": "Preorder Now",
  "preorder_button_description": "Ships in 6 weeks",
  "preorder_button_colors_enabled": true,
  "preorder_button_background_color": "#000000",
  "preorder_button_text_color": "#FFFFFF",

  // Badge Config
  "preorder_badge_enabled": true,
  "preorder_badge_text": "Preorder",
  "preorder_badge_background_color": "#000000",
  "preorder_badge_text_color": "#FFFFFF",

  // Shipping/Delivery
  "delivery_type": "exact_time", // or "anchor", "asap", "unknown"
  "delivery_exact_time": "2024-03-15",
  "delivery_after_n_intervals": 30, // days
  "preorder_shipping_text": "Ships {{ date }}",

  // Payment & Pricing
  "payment_options": [
    {
      "shopify_selling_plan_id": 111,
      "billing_title": "Pay in Full",
      "billing_description": "Full payment now",
      "pricing_type": "percentage", // or "fixed_amount", "no_discount"
      "pricing_percentage": 10
    },
    {
      "shopify_selling_plan_id": 222,
      "billing_title": "50% Deposit",
      "billing_description": "Pay {{ payment }} now, {{ remaining }} later",
      "billing_checkout_charge_type": "percentage",
      "billing_checkout_charge_percentage": 50
    }
  ],

  // Inventory & Limits
  "inventory_provider": "stoq", // or "shopify"
  "preorder_progress_bar_enabled": true,

  // Markets
  "markets_enabled": false,
  "shopify_market_ids": [1, 2, 3],

  // Countdown Timer
  "countdown_timer_enabled": true,
  "countdown_timer_use_schedule_dates": true,
  "schedule_start_date": "2024-03-01",
  "schedule_end_date": "2024-03-31"
}
```

### getPreorderAvailable / getPreorderSold / getPreorderTotal

Get real-time preorder inventory data to build custom availability displays.

```javascript
const available = window._RestockRocket.getPreorderAvailable(variantId);
const sold = window._RestockRocket.getPreorderSold(variantId);
const total = window._RestockRocket.getPreorderTotal(variantId);
```

#### Example - Custom Availability Counter:

```javascript
window.addEventListener('stoq:loaded', () => {
  const variantId = '{{ product.selected_or_first_available_variant.id }}';
  const plan = window._RestockRocket.getSellingPlan(variantId);

  if (plan) {
    const available = window._RestockRocket.getPreorderAvailable(variantId);
    const sold = window._RestockRocket.getPreorderSold(variantId);
    const total = window._RestockRocket.getPreorderTotal(variantId);

    document.getElementById('preorder-stats').innerHTML = `
      <div class="preorder-availability">
        <span class="sold">${sold} preorders</span> /
        <span class="total">${total} available</span>
      </div>
      <div class="remaining">${available} left at this price!</div>
    `;
  }
});
```



## 6. marketId

Returns the current Shopify market ID.

```javascript
const marketId = window._RestockRocket.marketId()
```

**Example:**
```javascript
const market = window._RestockRocket.marketId();
console.log("Current market:", market);

// Use for market-specific logic
if (market === "US") {
  // Show US-specific messaging
}
```



## Custom Events

STOQ dispatches custom events throughout the customer journey that you can tap into for analytics, custom UI updates, or integration with other tools. Every significant user action triggers an event with detailed context about what happened.

| Event | Description | Detail Properties |
| ---- |
| `stoq:loaded` | App loaded | pageType, enabled, settings |
| `stoq:modal:opened` | Modal opened | productId, variantId, modalType |
| `stoq:modal:closed` | Modal closed | productId, variantId |
| `stoq:intent:created` | Customer signed up | intentId, variantId, channel, customerEmail |
| `stoq:button:clicked` | Button clicked | productId, variantId, buttonType |
| `stoq:variant:changed` | Variant changed | variantId, available, hasSellingPlan |

#### Example - Track Signups with Analytics:

```javascript
window.addEventListener('stoq:intent:created', (event) => {
  const { intentId, variantId, channel } = event.detail;

  // Track in Google Analytics
  gtag('event', 'back_in_stock_signup', {
    'event_category': 'engagement',
    'event_label': `Variant ${variantId}`,
    'channel': channel
  });

  console.log(`Customer signed up via ${channel}`);
});
```



## Integration Examples

### Quick Add Button on Collection Page

```liquid
{% for product in collection.products %}
  <div class="product-item">
    <h2>{{ product.title }}</h2>
    <button class="quick-add-btn"
            data-product='{{ product | json | escape }}'
            data-variant-id="{{ product.selected_or_first_available_variant.id }}">
      Quick Add
    </button>
  </div>
{% endfor %}

<script>
window.addEventListener('stoq:loaded', () => {
document.querySelectorAll('.quick-add-btn').forEach(button => {
  button.addEventListener('click', function() {
      const productData = JSON.parse(this.getAttribute('data-product'));
    const variantId = this.getAttribute('data-variant-id');

    window._RestockRocket.openModal(productData, variantId);
  });
});
});
</script>
```



### Custom Variant Selector with Notify Button

```liquid
 <select id="variant-selector">
    {% for variant in product.variants %}
    <option value="{{ variant.id }}"
            {% if variant == current_variant %}selected{% endif %}>
        {{ variant.title }}
      </option>
    {% endfor %}
  </select>

<button id="add-to-cart-btn">Add to Cart</button>
<button id="notify-me-btn" style="display: none;">Notify Me</button>

<script>
const variantSelector = document.getElementById('variant-selector');
const addToCartBtn = document.getElementById('add-to-cart-btn');
const notifyMeBtn = document.getElementById('notify-me-btn');

function updateButtons(variantId) {
  const variant = {{ product.variants | json }}
    .find(v => v.id.toString() === variantId);

  if (variant && variant.available) {
    addToCartBtn.style.display = 'block';
    notifyMeBtn.style.display = 'none';
  } else {
    addToCartBtn.style.display = 'none';
    notifyMeBtn.style.display = 'block';
  }
}

variantSelector.addEventListener('change', function() {
  const selectedVariantId = this.value;
  updateButtons(selectedVariantId);
  window._RestockRocket.renderButtonForVariant(selectedVariantId);
});

notifyMeBtn.addEventListener('click', function() {
  window._RestockRocket.openModal();
});

// Initial setup
updateButtons(variantSelector.value);
</script>
```



### Custom Preorder Badge with Shipping Date

```liquid
<div id="preorder-info"></div>

<script>
window.addEventListener('stoq:loaded', () => {
  const variantId = '{{ product.selected_or_first_available_variant.id }}';
  const plan = window._RestockRocket.getSellingPlan(variantId);

  if (plan && plan.enabled) {
    const badge = document.createElement('div');
    badge.className = 'preorder-badge';
    badge.style.backgroundColor = plan.preorder_badge_background_color;
    badge.style.color = plan.preorder_badge_text_color;
    badge.innerHTML = `
      <span class="badge-text">${plan.preorder_badge_text}</span>
      <span class="shipping-date">Ships: ${plan.delivery_exact_time}</span>
    `;

    document.getElementById('preorder-info').appendChild(badge);
  }
});
</script>
```



### Custom preorder flow with multiple payment options

Build a custom payment selector for preorder offers with multiple payment plans. This example shows how to access payment options and handle selection changes.

```liquid
<div id="custom-preorder-container">
  <div id="payment-options"></div>
  <div id="preorder-details"></div>
  <button id="add-to-cart">Add to Cart</button>
</div>

<script>
window.addEventListener('stoq:loaded', () => {
  const variantId = '{{ product.selected_or_first_available_variant.id }}';
  const plan = window._RestockRocket.getSellingPlan(variantId);

  if (!plan || !plan.payment_options || plan.payment_options.length === 0) {
    return;
  }

  const container = document.getElementById('payment-options');
  let selectedPlanId = plan.payment_options[0].shopify_selling_plan_id;

  // Render payment options
  plan.payment_options.forEach((option, index) => {
    const optionDiv = document.createElement('div');
    optionDiv.className = 'payment-option';
    optionDiv.innerHTML = `
      <input type="radio"
             name="payment"
             id="payment-${index}"
             value="${option.shopify_selling_plan_id}"
             ${index === 0 ? 'checked' : ''}>
      <label for="payment-${index}">
        <strong>${option.billing_title}</strong>
        <span>${option.billing_description}</span>
        ${option.pricing_type !== 'no_discount' ?
          `<span class="discount">${option.pricing_percentage}% off</span>` : ''}
      </label>
    `;

    optionDiv.querySelector('input').addEventListener('change', (e) => {
      selectedPlanId = e.target.value;
      updatePreorderDetails(option);
    });

    container.appendChild(optionDiv);
  });

  // Update details display
  function updatePreorderDetails(option) {
    const details = document.getElementById('preorder-details');
    const available = window._RestockRocket.getPreorderAvailable(variantId);

    details.innerHTML = `
      <p><strong>Shipping:</strong> ${plan.delivery_exact_time}</p>
      <p><strong>Available:</strong> ${available} units</p>
      <p><strong>Payment:</strong> ${option.billing_description}</p>
    `;
  }

  // Initialize with first option
  updatePreorderDetails(plan.payment_options[0]);

  // Handle add to cart with selected plan
  document.getElementById('add-to-cart').addEventListener('click', () => {
    const properties = window._RestockRocket.getLineItemProperties(
      plan,
      variantId,
      true // isPreorderItem
    );

    // Add to cart with properties
    fetch('/cart/add.js', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        id: variantId,
        quantity: 1,
        selling_plan: selectedPlanId,
        properties: properties
      })
    })
    .then(response => response.json())
    .then(data => {
      console.log('Added to cart:', data);
      window.location.href = '/cart';
    });
  });
});
</script>

<style>
.payment-option {
  border: 2px solid #ddd;
  padding: 15px;
  margin: 10px 0;
  border-radius: 8px;
  cursor: pointer;
}
.payment-option input:checked + label {
  font-weight: bold;
}
.discount {
  background: #00a651;
  color: white;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 12px;
}
</style>
```



### Dynamic preorder progress bar

Show real-time preorder demand with a custom progress bar.

```liquid
<div id="preorder-progress"></div>

<script>
window.addEventListener('stoq:loaded', () => {
  const variantId = '{{ product.selected_or_first_available_variant.id }}';
  const plan = window._RestockRocket.getSellingPlan(variantId);

  if (plan) {
    const sold = window._RestockRocket.getPreorderSold(variantId);
    const total = window._RestockRocket.getPreorderTotal(variantId);
    const available = window._RestockRocket.getPreorderAvailable(variantId);
    const percentage = Math.round((sold / total) * 100);

    document.getElementById('preorder-progress').innerHTML = `
      <div class="progress-header">
        <span>🔥 ${sold} preorders placed</span>
        <span>${available} remaining</span>
      </div>
      <div class="progress-bar">
        <div class="progress-fill" style="width: ${percentage}%"></div>
      </div>
      <p class="progress-label">${percentage}% claimed</p>
    `;
  }
});
</script>

<style>
.progress-bar {
  width: 100%;
  height: 20px;
  background: #f0f0f0;
  border-radius: 10px;
  overflow: hidden;
  margin: 10px 0;
}
.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #00a651, #00d563);
  transition: width 0.3s ease;
}
.progress-header {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  margin-bottom: 5px;
}
</style>
```



### Countdown timer for limited preorder

Create a custom countdown timer for time-sensitive preorder offers.

```liquid
<div id="preorder-countdown"></div>

<script>
window.addEventListener('stoq:loaded', () => {
  const variantId = '{{ product.selected_or_first_available_variant.id }}';
  const plan = window._RestockRocket.getSellingPlan(variantId);

  if (plan && plan.countdown_timer_enabled && plan.schedule_end_date) {
    const endDate = new Date(plan.schedule_end_date);

    function updateCountdown() {
      const now = new Date();
      const diff = endDate - now;

      if (diff <= 0) {
        document.getElementById('preorder-countdown').innerHTML =
          '<p>Preorder has ended</p>';
        return;
      }

      const days = Math.floor(diff / (1000 * 60 * 60 * 24));
      const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
      const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
      const seconds = Math.floor((diff % (1000 * 60)) / 1000);

      document.getElementById('preorder-countdown').innerHTML = `
        <div class="countdown">
          <div class="countdown-unit">
            <span class="value">${days}</span>
            <span class="label">Days</span>
          </div>
          <div class="countdown-unit">
            <span class="value">${hours}</span>
            <span class="label">Hours</span>
          </div>
          <div class="countdown-unit">
            <span class="value">${minutes}</span>
            <span class="label">Mins</span>
          </div>
          <div class="countdown-unit">
            <span class="value">${seconds}</span>
            <span class="label">Secs</span>
          </div>
        </div>
        <p class="countdown-text">Preorder ends soon!</p>
      `;
    }

    updateCountdown();
    setInterval(updateCountdown, 1000);
  }
});
</script>

<style>
.countdown {
  display: flex;
  gap: 15px;
  justify-content: center;
  margin: 20px 0;
}
.countdown-unit {
  text-align: center;
  min-width: 60px;
}
.countdown-unit .value {
  display: block;
  font-size: 32px;
  font-weight: bold;
  background: #000;
  color: #fff;
  padding: 10px;
  border-radius: 8px;
}
.countdown-unit .label {
  display: block;
  margin-top: 5px;
  font-size: 12px;
  text-transform: uppercase;
}
</style>
```



---

## Full documentation

For complete API reference, detailed parameters, response schemas, and interactive testing:** https://docs.stoqapp.com/frontend/docs/javascript-api/ & https://docs.stoqapp.com/frontend/docs/custom-events/. **If you need any assistance with custom integrations or using the JavaScript API, feel free to contact our 24/7 live support. We're happy to help!