> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sequenzy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Shopify Integration

> Connect Shopify to sync products, customers, orders, storefront browse events, back-in-stock requests, and replenishment reminders.

# Shopify Integration

You can connect a Shopify store to Sequenzy from `Settings -> Integrations`.

Once connected, Sequenzy syncs store data, listens for Shopify webhooks, and uses the provider-neutral commerce event pipeline to capture known-customer storefront browse events. Those events can power product-aware automation such as replenishment reminders, back-in-stock emails, and personalized recommendations.

## What It Does

* Sync products, variants, prices, images, stock state, and product URLs
* Keep the catalog fresh in real time through product create/update/delete webhooks
* Sync Shopify customers and order revenue onto subscriber profiles
* Track order, checkout, customer, refund, fulfillment, and stock events
* Track shipment stages (shipped, out for delivery, delivered, delivery failed) with tracking data
* Reverse subscriber revenue and campaign attribution when orders are refunded or cancelled
* Track known-customer product views, add-to-cart, collection views, and searches through the Shopify Web Pixels API
* Trigger browse abandonment events when a viewer doesn't buy within the browse window
* Trigger price drop events for recent viewers when a product's price decreases
* Capture back-in-stock requests from the Shopify storefront block
* Trigger replenishment reminder events after a product-specific delay
* Create dynamic Shopify discount codes from sequence discount actions
* Expose `recommendedProducts` merge variables for product recommendation blocks
* Show purchased products on subscriber profiles alongside Stripe products

## Required Scopes

Sequenzy requires these Shopify OAuth scopes:

| Scope                  | Used For                                    |
| ---------------------- | ------------------------------------------- |
| `read_customers`       | Customer sync and customer profile updates  |
| `read_orders`          | Order events, revenue, and purchased items  |
| `read_fulfillments`    | Shipment tracking events                    |
| `read_products`        | Product catalog, variants, pricing, images  |
| `read_inventory`       | Inventory-level updates for stock webhooks  |
| `read_checkouts`       | Checkout events for recovery and automation |
| `read_discounts`       | Discount lookup and validation              |
| `write_discounts`      | Discount code creation                      |
| `read_pixels`          | Web pixel lookup during Shopify sync        |
| `write_pixels`         | Web pixel activation and settings updates   |
| `read_customer_events` | Shopify Web Pixels customer events          |

When you reconnect Shopify, Sequenzy checks the scopes Shopify actually granted to the saved token. If any required scope is missing, reconnect the store from Settings so Shopify can grant the latest permissions.

## Storefront Browse Tracking

Sequenzy includes a Shopify web pixel extension that subscribes to Shopify's `product_viewed` and `product_added_to_cart` standard events. The pixel is one adapter for the generic commerce customer event ingestion endpoint. During Shopify post-connect sync, Sequenzy creates or updates the store's app pixel settings with this endpoint:

```text theme={null}
https://api.sequenzy.com/api/webhooks/commerce/shopify/{companyId}/customer-events
```

The previous Shopify-specific endpoint, `/api/webhooks/shopify/{companyId}/customer-events`, remains available as a compatibility alias. The managed Shopify pixel includes a signed verification token in each request; the endpoint validates the token, connected company, and shop, queues the event, and returns immediately. A worker records browse events only for known subscribers with an email in the pixel event context. Anonymous browse sessions are skipped in the MVP.

Custom storefront adapters can post the same shape to:

```text theme={null}
https://api.sequenzy.com/api/webhooks/commerce/{provider}/{companyId}/customer-events
```

Use one of the supported product providers as `{provider}` (`shopify`, `woocommerce`, `manual`, `api`, or `stripe`). Shopify and WooCommerce payloads must include a valid signed `verificationToken` for the active integration. Generic adapters such as `api`, `manual`, or `stripe` must include a Sequenzy API key in `Authorization: Bearer ...` or `x-api-key`; the key must belong to the route company. Generic `api` adapters can send events without Shopify-specific fields.

Browse events trigger these internal events:

| Shopify Event           | Sequenzy Event                    |
| ----------------------- | --------------------------------- |
| `product_viewed`        | `ecommerce.product_viewed`        |
| `product_added_to_cart` | `ecommerce.product_added_to_cart` |
| `collection_viewed`     | `ecommerce.collection_viewed`     |
| `search_submitted`      | `ecommerce.search_submitted`      |

Both events include product matching fields for automations:

```json theme={null}
{
  "provider": "shopify",
  "source": "shopify_web_pixel",
  "providerAccountId": "store.myshopify.com",
  "providerEventName": "product_viewed",
  "providerProductId": "788032119674292922",
  "providerVariantId": "45123456789123",
  "productTitle": "Protein Powder",
  "variantTitle": "Vanilla",
  "product": {
    "provider": "shopify",
    "providerProductId": "788032119674292922",
    "providerVariantId": "45123456789123",
    "title": "Protein Powder",
    "imageUrl": "https://cdn.example.com/product.png",
    "url": "https://store.example.com/products/protein-powder",
    "price": 88.5,
    "priceCents": 8850,
    "currency": "USD"
  }
}
```

## Personalized Recommendations

Campaign, test-email, sequence, and A/B test sends receive a generated `recommendedProducts` merge variable. The MVP ranks products across commerce providers in this order:

1. Recent products the subscriber viewed or added to cart
2. Products browsed by similar customers who viewed the same products
3. Popular recently browsed products from the same connected catalog
4. Recent in-stock catalog products

Products the subscriber already purchased are excluded when the purchase event contains commerce line items with provider and product IDs.

Use these variables in email content:

```text theme={null}
{{recommendedProducts.0.title}}
{{recommendedProducts.0.imageUrl}}
{{recommendedProducts.0.price}}
{{recommendedProducts.0.url}}
```

The same array is also available under `{{variables.recommendedProducts.0.title}}`.

## Synced Data

### Products

The product catalog includes:

* Product title, description, handle, image, URL, and stock state
* Lowest product price and compare-at price when available
* Variant IDs, titles, SKUs, option values, prices, images, and inventory state
* Replenishment settings stored per product

You manage synced products from `Settings -> Products`. Product rows show stock, price, variants, back-in-stock waitlists, and pending replenishment reminders.

### Subscribers

Shopify customer and order data is stored as subscriber attributes:

| Attribute     | Type   | Description                              |
| ------------- | ------ | ---------------------------------------- |
| `ordersCount` | Number | Total known Shopify orders               |
| `totalOrders` | Number | Alias for total known Shopify orders     |
| `totalSpent`  | Number | Total known Shopify spend in store money |
| `ltv`         | Number | Lifetime value used for segmentation     |
| `aov`         | Number | Average order value                      |

Older subscribers may still have `totalRevenue` or `averageOrderValue`. Sequenzy treats those as legacy fallbacks for `ltv` and `aov` instead of showing duplicate fields in subscriber views.

### Phone and SMS consent

Customer sync and customer webhooks also carry each customer's phone number
and SMS marketing consent. A customer who opted into SMS marketing on your
store (for example at checkout) becomes SMS-subscribed in Sequenzy with
consent source `shopify` and Shopify's consent timestamp; an opt-out on
Shopify unsubscribes them. A phone number without an explicit opt-in is
stored but never treated as consent. This requires approved phone field
access for protected customer data on your store - without it, the sync
skips phone and consent and everything else still works. See
[SMS consent](/concepts/sms#shopify-sms-consent-sync) for details.

## Order Events

Shopify order webhooks trigger `ecommerce.order_placed` with product line item data:

```json theme={null}
{
  "orderId": "1002",
  "totalPriceCents": 8850,
  "currency": "USD",
  "orderedAt": "2026-04-02T00:00:00.000Z",
  "lineItems": [
    {
      "provider": "shopify",
      "lineItemId": "line-1",
      "providerProductId": "788032119674292922",
      "providerVariantId": "45123456789123",
      "sku": "VANILLA-1",
      "title": "Protein Powder",
      "variantTitle": "Vanilla",
      "quantity": 1,
      "priceCents": 8850
    }
  ]
}
```

Sequenzy uses `lineItems[].providerProductId` and, when variant-level matching is enabled, `lineItems[].providerVariantId` to match follow-up automation against the exact product that was purchased.

Checkout webhooks trigger `ecommerce.checkout_started` once per checkout, as soon as the buyer's email is known. The event carries the line items at that moment plus `abandonedCheckoutUrl` so recovery emails can link straight back to the checkout. Completed checkouts never trigger the event - the order webhook takes over.

## Shipment Events

Fulfillment webhooks track each shipment through its delivery lifecycle. Every stage triggers exactly once per fulfillment:

| Sequenzy Event                     | When                                             |
| ---------------------------------- | ------------------------------------------------ |
| `ecommerce.order_shipped`          | A fulfillment is created or confirmed in transit |
| `ecommerce.order_out_for_delivery` | Carrier reports the shipment is out for delivery |
| `ecommerce.order_delivered`        | Carrier reports the shipment as delivered        |
| `ecommerce.delivery_failed`        | Carrier reports a failed or attempted delivery   |

Events include `trackingNumber`, `trackingUrl`, `trackingCompany`, `shipmentStatus`, and the fulfilled `lineItems`, so shipping confirmation emails can render tracking links and review requests can wait for the actual delivery date. Carrier stage updates (`out_for_delivery`, `delivered`) require a Shopify-supported tracking carrier; untracked fulfillments still trigger `ecommerce.order_shipped`. Existing stores must reconnect once so Sequenzy can request the `read_fulfillments` scope.

## Refunds and Cancellations

Refund and cancellation webhooks keep revenue honest:

* `refunds/create` subtracts the refunded amount from the subscriber's `totalSpent`, `ltv`, and `aov` attributes
* `orders/cancelled` also decrements `ordersCount`, for orders Sequenzy previously counted
* Attributed campaign and sequence revenue is adjusted with a matching negative conversion, so dashboards net out refunded orders instead of overstating

## Browse Abandonment

When a known subscriber views a product through the browse pixel and then goes quiet, Sequenzy triggers `ecommerce.browse_abandoned` after a configurable delay (default 2 hours). The check is skipped when the subscriber placed any order, started a checkout, or added the viewed product to their cart since the view, and at most one browse abandonment event fires per subscriber per cooldown window (default 24 hours). The event carries the viewed product so recovery emails can show exactly what caught their eye.

## Price Drop Alerts

When a product update lowers a variant's price by at least 5% (configurable), Sequenzy triggers `ecommerce.price_drop` for subscribers who viewed the product or added it to their cart in the last 30 days. Events include `oldPriceCents`, `newPriceCents`, `percentOff`, and the product payload for rendering. Each subscriber receives at most one price drop event per product per 7 days.

If no sequence is set up for `ecommerce.price_drop`, Sequenzy sends a default price-drop email automatically - a product card with the old price struck through, the new price, the discount percent, and a link to the product. Active subscribers only; unsubscribed contacts never receive it. Create a sequence triggered by `ecommerce.price_drop` to replace the default email with your own.

## Automation Settings

Browse abandonment and price drop are on by default with sensible timing. Adjust or disable them per store:

* **Dashboard**: Settings -> Integrations -> Shopify -> Settings
* **CLI**: `sequenzy shopify settings get` / `sequenzy shopify settings update`
* **MCP**: `get_shopify_automation_settings` / `update_shopify_automation_settings`
* **API**: `GET`/`PUT /api/v1/shopify/automation-settings`

Settings survive store reconnects and token refreshes.

## Replenishment Reminders

Replenishment reminders start from synced products:

1. Open `Settings -> Products`.
2. Choose a Shopify product.
3. Turn on replenishment.
4. Pick how many days after purchase the product usually runs out.
5. Choose whether matching should use any variant of the product or the exact purchased variant.

When someone buys a configured product, Sequenzy queues a reminder. If the subscriber buys the same product again before the reminder is due, the reminder is skipped. When the reminder fires, it triggers `ecommerce.replenishment_due`.

The event includes the same product matching fields:

```json theme={null}
{
  "provider": "shopify",
  "providerProductId": "788032119674292922",
  "providerVariantId": "45123456789123",
  "replenishmentScope": "variant",
  "orderedAt": "2026-04-02T00:00:00.000Z",
  "sourceOrderId": "1002",
  "product": {
    "providerId": "788032119674292922",
    "providerVariantId": "45123456789123",
    "title": "Protein Powder",
    "variantTitle": "Vanilla",
    "imageUrl": "https://cdn.example.com/product.png",
    "url": "https://store.example.com/products/protein-powder",
    "price": "$88.50",
    "priceCents": 8850,
    "currency": "USD"
  }
}
```

Sequences started by `ecommerce.replenishment_due` get a default stop condition: they stop when the same product is purchased again.

## Enable the Back in stock theme block

After installing the Shopify app, add the Sequenzy theme app block to a product template:

1. In Sequenzy, open `Settings -> Integrations -> E-Commerce -> Shopify`.
2. Click **Add Back in stock block**. This opens Shopify's theme editor on the product template with the app block ready to add.
3. In the Shopify theme editor, add **Back in stock** from **Apps** or **Sequenzy Notify Me**.
4. Place it near the product form, click **Save**, and preview an out-of-stock product or unavailable variant.

Sequenzy uses a product-page app block for back-in-stock capture. No separate app embed is required for this theme extension.

The block is hidden on in-stock variants by default. It appears when the selected variant is unavailable, unless you change the block's **Show block when** setting.

If the direct button is not available, use this manual path in Shopify:

`Online Store -> Themes -> Customize -> Products -> Default product -> Add block -> Apps -> Back in stock`

The deep link format is:

```text theme={null}
https://{shop}.myshopify.com/admin/themes/current/editor?template=product&addAppBlockId=81144e992d5cb6078bb79fbeb74236a2%2Fback-in-stock&target=newAppsSection
```

## Back-In-Stock Requests

The Shopify storefront block captures customer email, product ID, variant ID, product title, and variant title for out-of-stock items. Sequenzy records the request and triggers `ecommerce.back_in_stock_requested`.

When a synced product variant returns to stock, Sequenzy marks active waitlist entries as notified and triggers `ecommerce.back_in_stock`.

```json theme={null}
{
  "provider": "shopify",
  "providerProductId": "788032119674292922",
  "providerVariantId": "45123456789123",
  "productTitle": "Protein Powder",
  "variantTitle": "Vanilla",
  "productUrl": "https://store.example.com/products/protein-powder",
  "imageUrl": "https://cdn.example.com/product.png",
  "requestedAt": "2026-04-01T12:00:00.000Z",
  "backInStockAt": "2026-04-02T08:00:00.000Z"
}
```

Sequences started by `ecommerce.back_in_stock` get a default stop condition: they stop if the same item is purchased or if the same item goes out of stock again.

## Subscriber Profiles

Subscriber profiles show commerce context from Shopify:

* Purchased products from order line items, including product image, variant, quantity, price, and last purchase time
* Pending replenishment reminders
* Active back-in-stock waitlist entries
* Revenue metrics such as `ltv` and `aov`

Revenue metrics come from Shopify customer totals when available. Purchased
product rows come from order line items, so they can be less complete than LTV
and AOV if Shopify does not grant all historical order access. By default,
Shopify order objects expose only the last 60 days unless the app is approved
for `read_all_orders`.

Pending replenishment rows link to the subscriber profile when the subscriber is known.
