---
title: "Storefront triggers - run Shopify Flow on what shoppers do"
description: "Start a Shopify Flow workflow from shopper behaviour: product views, cart changes, searches with no results, out-of-stock variants and checkout errors, using the app's Storefront Pixel."
canonical: "https://docs.workflow-trigger-extensions.app/storefront-triggers"
---

# Storefront triggers

Every other trigger in this app starts from something that happened in your **admin**: an order was paid, a product was updated, a metafield changed. Shopify tells us, and your workflow runs.

Storefront triggers start from something a **shopper** did. A product page was viewed, an item was added to the cart, a search returned nothing, a discount code was rejected at checkout. Shopify does not send webhooks for any of that, so the app collects it with a **Web Pixel** that runs on your storefront.

That difference is worth holding on to, because it explains most of what follows: what the app can see, what it cannot, and why these triggers can be far higher volume than the ones you are used to.

> [!TIP]
> **Nothing is collected until you opt in**
> Two things have to be true before a single storefront event reaches us: you have granted **Storefront Behaviour Access** on the Permissions page, and at least one storefront trigger is switched on.
>
> Some storefront triggers need a second permission, because Shopify Flow refuses an event that refers to a record the app is not allowed to read. The ones that send a product (Storefront Product Viewed, Added to Cart, Removed from Cart, Storefront Search and Out-of-Stock Variant Viewed) also need **Product, Variant & Collection Data Access**, and Signed-In Customer Visited also needs **Customer Data Access**. The [Complete trigger reference](https://docs.workflow-trigger-extensions.app/trigger-reference.md) lists them all.
>
> Every storefront trigger starts **off**. One is switched on when you flip its switch on the Triggers page, when you turn on a Shopify Flow workflow that uses it, or when you use **Enable all** on the Triggers page, which includes this group.

> [!WARNING]
> **These are high-volume triggers**
> A busy store can produce more product views in a day than order updates in a year. **Every storefront event counts towards your plan allowance**, exactly like a webhook event.
>
> Switch on only the ones a workflow actually uses, and check [Plans and usage](https://docs.workflow-trigger-extensions.app/plans-and-usage.md) before enabling Storefront Product Viewed on a high-traffic store. **Enable all** on the Triggers page includes the storefront triggers, so search first if you only mean to bulk-enable admin triggers. You can see live counts on the Event History page.

## The Storefront Pixel

When your first storefront trigger is switched on, the app installs a **Web Pixel** on your storefront automatically. There is no theme edit and no code to paste.

The pixel only subscribes to the events your enabled triggers need. If you enable Storefront Search and nothing else, product views are never sent, never received and never counted. Switching every storefront trigger off again leaves the pixel installed but idle: it subscribes to no events and sends nothing.

## Shopper consent

The pixel respects your store's customer privacy settings. It needs **analytics consent**, and Shopify only runs it for a shopper who has given that consent. Without it nothing is collected in the browser, nothing reaches us, no allowance is used and no workflow runs.

This applies at checkout too, so Checkout Error Shown and Checkout Discount Code Rejected need the same consent as the storefront triggers.

It also means storefront triggers under-report by design in regions where consent is opt-in. That is the correct behaviour, not a bug to work around.

> [!WARNING]
> **No storefront events? Check your cookie banner**
> How a shopper's consent reaches the pixel depends on which banner your store uses.
>
> - **Shopify's cookie banner** (Settings > Customer privacy in your Shopify admin): the shopper has to **accept** it, or allow analytics in its preferences. A shopper who declines, or never answers the banner, sends no storefront events.
> - **A consent app or your own banner**: it has to pass the shopper's choice on to Shopify through Shopify's **Customer Privacy API**. Many consent apps built for Shopify can do this, usually behind a setting for Shopify's Customer Privacy API or Shopify pixels - make sure it is switched on. A banner that only manages your theme's own scripts and never tells Shopify leaves every shopper without consent as far as Shopify pixels are concerned, so storefront triggers never fire, even for shoppers who clicked accept.
>
> If you built the banner yourself, pass the shopper's choice to Shopify when they make it, as shown below.

```javascript
window.Shopify.loadFeatures(
  [{ name: "consent-tracking-api", version: "0.1" }],
  (error) => {
    if (error) return;
    // Call this when the shopper makes a choice in your banner.
    // Pass what they actually chose - storefront triggers need analytics.
    window.Shopify.customerPrivacy.setTrackingConsent(
      { analytics: true, marketing: false, preferences: false },
      () => {},
    );
  },
);
```

Shopify's reference for this is the [Customer Privacy API](https://shopify.dev/docs/api/customer-privacy).

**When you test a storefront trigger**, accept the cookie banner first and use a browser without an ad blocker. A private window starts without consent every time, so a trigger that works in your normal browser can look broken there. To check that the pixel itself is installed, open **Settings > Customer events** in your Shopify admin.

> [!WARNING]
> **About the permission dialog Shopify shows you**
> Storefront triggers need the **Storefront Behaviour Access** permission. Behind it is Shopify's `read_customer_events` scope, granted together with `write_pixels` so the app can install its pixel. When you grant it, Shopify shows a broad warning about sensitive data, device and activity information. That wording is Shopify's, is the same for every app requesting this scope, and describes the widest thing the scope *could* allow - not what this app does.
>
> **What the app actually sends to its own servers:** commerce context - product, variant, collection, cart and checkout ids with the product title, vendor, type and URL, quantities and prices, the search term with up to ten matching products, and the text of a checkout error message - plus the customer id when a shopper is signed in, the company and location id and name for a signed-in B2B buyer, and the IP address that is part of any web request.
>
> **What it never sends:** shopper names, email addresses, phone numbers or postal addresses, or anything a shopper types into a checkout field - discount codes and gift card codes included.
>
> Storefront events sit in Event History next to your other events and are deleted on the same schedule. See [Permissions and data access](https://docs.workflow-trigger-extensions.app/permissions-and-data-access.md) for how permissions work across the app.

## The triggers

| Trigger | Starts when |
| --- | --- |
| Storefront Product Viewed | A shopper opens a product page |
| Storefront Collection Viewed | A shopper opens a collection page |
| Storefront Cart Viewed | A shopper opens the cart page |
| Storefront Product Added to Cart | An item is added to the cart |
| Storefront Product Removed from Cart | An item is removed from the cart |
| Storefront Checkout Started | A shopper begins checkout |
| Storefront Search | A search returns at least one result |
| Storefront Search Returned No Results | A search returns nothing |
| Out-of-Stock Variant Viewed | A shopper views a product page whose shown variant is sold out |
| Checkout Error Shown | Checkout displays an error to the shopper |
| Checkout Discount Code Rejected | A discount code is refused at checkout |
| B2B Buyer Visited | A signed-in B2B buyer visits the storefront |
| Signed-In Customer Visited | A signed-in customer visits the storefront |
| Custom Storefront Trigger | Your own event, published from your theme |

> [!WARNING]
> **Storefront Cart Viewed does not fire for cart drawers**
> Shopify's cart view event fires on the **cart page** (`/cart`). Most modern themes open a **cart drawer** instead and never navigate there, so on those themes this trigger will rarely or never fire.
>
> This is a limit of what Shopify reports, not something the app can detect. If your theme uses a drawer, publish your own event when it opens and use [Custom Storefront Triggers](https://docs.workflow-trigger-extensions.app/custom-storefront-triggers.md) instead.

## Out-of-Stock Variant Viewed

This one is worth reading closely, because it is the trigger most likely to behave differently from what the name suggests.

It is a **restock demand signal**: it tells you that a real shopper looked at something you cannot sell them. Paired with a Flow workflow it can tag the product, notify buying, or add the shopper to a waitlist.

It is **derived**, not reported. The shopper's browser has no idea what your inventory is, so the pixel sends a plain product view and the app decides against its own inventory records.

### It checks a variant, not the whole product

The trigger fires on the variant the product page is showing, not on the product as a whole. A product with four in-stock variants and one sold-out variant fires only when the sold-out one is the one being shown.

That is why it is called Out-of-Stock **Variant** Viewed, and why the trigger gives your workflow both the variant and the product it belongs to.

### Switching variant after the page loads is not detected

Shopify reports a product view when the shopper **lands on the page**. Clicking through the variant picker afterwards is a change of state inside the page, not a new page view, so Shopify does not report it and the app never learns about it.

In practice: a link straight to the sold-out variant (a `?variant=` URL, or arriving from a collection where it is the shown option) fires. Landing on an in-stock default and then selecting the sold-out one does not.

If you need that, publish your own event on variant change from your theme with [Custom Storefront Triggers](https://docs.workflow-trigger-extensions.app/custom-storefront-triggers.md).

### Out of stock means zero or less across all locations

The app adds up the available quantity at **every** location that stocks the variant. The trigger fires when that total is zero or below.

Two consequences worth planning for:

- **Locations that do not serve your online store still count.** Stock sitting in a warehouse or a retail location that is not a sales channel for this product makes the total look healthy, so the trigger stays quiet even though the shopper cannot buy it.
- **Overselling is not considered.** A variant set to continue selling when out of stock is still purchasable at zero, so the trigger fires while the shopper can in fact complete the order.

### Enabling it also enables Storefront Product Viewed

It has to. The out-of-stock decision is made from a product view, so the app must receive every product view to find the few that matter.

That means **every product view counts towards your allowance**, not only the out-of-stock ones. On a high-traffic store this is the single most expensive trigger in the app, and the cost is driven by your traffic, not by how often you sell out.

We force the dependency on rather than hiding it, so that the number on your usage page is never a surprise.

### It needs inventory to have been synced

The app compares against its own inventory records. If a variant has never been synced, the app does not know its stock level and stays quiet rather than guessing - reporting an unsynced variant as sold out would be worse than reporting nothing.

Inventory syncs automatically when you enable the trigger. You can re-run it any time from the Triggers page.

## Searches

Search gives you two triggers, and a single search fires **exactly one** of them:

- **Storefront Search Returned No Results** when the search found nothing. This is the one most stores want: it is a list of things shoppers expected you to sell.
- **Storefront Search** when the search found something.

Because they are mutually exclusive you can safely enable both without double counting. Storefront Search also carries the **first result** as a product reference, so a workflow can act on what the shopper most likely saw.

## Visit triggers

**B2B Buyer Visited** and **Signed-In Customer Visited** fire when a signed-in shopper is on your storefront. They are the way to react to a customer coming back - re-engagement, account-manager alerts, a note on the record.

A shopper browsing ten pages is one visit, not ten events. The app collapses repeats into a **visit window** you choose per trigger with the settings icon next to its switch. Inside that window a shopper counts once.

- **B2B Buyer Visited** collapses per **company**, so two buyers from the same company in one window are one visit.
- **Signed-In Customer Visited** collapses per **customer**.

Set the window to zero to fire on every page view. On a store with signed-in traffic that is a lot of events, so start high and lower it if you need finer granularity.

## Checkout triggers

**Checkout Error Shown** fires whenever checkout displays an error to a shopper: an address that will not validate, a payment that was declined, a field that will not accept what was typed. It is a way to find out that checkout is failing without waiting for a support ticket.

**Checkout Discount Code Rejected** narrows that to one case: checkout refused a discount code. That is usually an expired campaign, a code shared beyond its limits, or a code from an email that was never activated - all things worth knowing quickly.

Shopify does **not** tell apps which code was typed, so the trigger carries Shopify's rejection message (in the shopper's language) but not the code itself. Clicking apply with an empty field shows the same message, so the two cannot be told apart. Use it to spot a rise in rejected codes, not to identify a specific campaign.

## Next steps

- [Custom Storefront Triggers](https://docs.workflow-trigger-extensions.app/custom-storefront-triggers.md) - publish your own events from your theme, for cart drawers and anything else Shopify does not report.
- [Plans and usage](https://docs.workflow-trigger-extensions.app/plans-and-usage.md) - what counts as an event, and how the allowance works.
- [Permissions and data access](https://docs.workflow-trigger-extensions.app/permissions-and-data-access.md) - what each permission unlocks and what the app reads.
- [Event history and troubleshooting](https://docs.workflow-trigger-extensions.app/event-history-and-troubleshooting.md) - see what fired and what it carried.
