---
title: "How to add guides and surveys to a Shopify store"
description: "Add the guides and surveys SDK beside Shopify's app-owned analytics. Target a storefront survey and verify a saved synthetic answer."
canonical: "https://amplitude.com/en-us/build/shopify-guides-surveys"
---

# How to add guides and surveys to a Shopify store

Add the guides and surveys SDK beside Shopify's app-owned analytics. Target a storefront survey and verify a saved synthetic answer.

Source: https://amplitude.com/en-us/build/shopify-guides-surveys

---

# How to add guides and surveys to a Shopify store

Add the guides and surveys SDK beside Shopify's app-owned analytics. Target a storefront survey and verify a saved synthetic answer.

- Shopify
- Online Store theme
- Amplitude app
- 25 min

Chris Tam · Last updated 19 October 2026

Shopify storefront events show which products a visitor explored. [Guides and surveys](https://amplitude.com/guides-and-surveys) let you ask what information the visitor still needs while they are on that page.

In this tutorial, I'll attach the separate engagement plugin, target a test survey to the offer page, and check a synthetic answer in Amplitude.

## What you'll build

A separate engagement plugin using the app-connected project, with a storefront survey and an explicitly verified response.

## Prerequisites

- An Online Store Shopify test store and permission to install the Amplitude app and edit a duplicated theme. These tutorials target a Liquid theme, not a headless Hydrogen storefront.
- A test product and an Amplitude test project. Get its public API key in Settings > Projects > your project > General > Manage next to API Key. Admins and Managers can view it.
- A storefront privacy configuration that permits this synthetic test. Check initial refusal, grant, and withdrawal on the actual store. The custom code below controls only its own additions; it does not configure or stop the app's collection.
- Before using browser snippets, confirm the app exposes Browser SDK 2 methods on `window.amplitude` in the top storefront window. Check the installed version in Instrumentation Explorer. Do not add another initializer to make a missing global appear.

An Amplitude account and Shopify test store are required. [Sign up for Amplitude](https://app.amplitude.com/signup); check the product limits for your project before testing.

## 1. Keep the app-owned analytics setup

The [Shopify installation reference](https://amplitude.com/docs/data/amplitude-shopify-plugin) and [guides and surveys SDK reference](https://amplitude.com/docs/sdks/guides-and-surveys/sdk) prescribe a separate SDK for guides and surveys. Keep the app's analytics, replay, experiment configuration, and [shared offer section](/build/shopify-web-analytics). Confirm the existing global supports Browser SDK 2's track, add, and getDeviceId methods before using this plugin path.

Browser Console on the storefront, after the app loads

```javascript
// Browser Console on the storefront, after the app loads
({
  track: typeof window.amplitude?.track,
  add: typeof window.amplitude?.add,
  deviceId: window.amplitude?.getDeviceId?.()
});
```

Use the same project key and region as the app. An absent global is a failed prerequisite, not permission to add another analytics installation. For a custom analytics proxy, use the SDK's standalone proxy instructions instead of this plugin file unchanged.

## 2. Add the consent-aware engagement file

Create `assets/amp-shopify-guides.js` below. It waits for the app's SDK, sends one setup diagnostic, and requires an accepted HTTP 200 result from that existing SDK track call before attaching the plugin. That avoids adding the plugin during analytics startup. If the diagnostic is rejected, guides stay unloaded. The first setup event precedes plugin attachment; don't use it as an On event tracked trigger.

assets/amp-shopify-guides.js

```javascript
// assets/amp-shopify-guides.js
// Preserve the app-owned SDK/init, amp-shopify-events.js, replay privacy, and experiment target.
(() => {
  if (window.__ampShopifyGuides) return;
  window.__ampShopifyGuides = true;
  const config = JSON.parse(document.getElementById('amp-shopify-config').textContent);
  const allowed = () => window.Shopify?.customerPrivacy?.analyticsProcessingAllowed() === true;
  let starting;
  let attached = false;
  let stopped = false;
  let permissionSeen = false;
  const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
  async function start() {
    if (!allowed() || stopped) return;
    if (starting) return starting;
    starting = (async () => {
      const deadline = Date.now() + 15000;
      while (typeof window.amplitude?.track !== 'function' ||
             typeof window.amplitude?.add !== 'function' ||
             !window.amplitude?.getDeviceId?.()) {
        if (!allowed()) return;
        if (Date.now() >= deadline) throw new Error('App-owned Browser SDK 2 is not ready.');
        await wait(100);
      }
      const analytics = window.amplitude;
      // A completed track call waits for the existing SDK pipeline. Never reinitialize it.
      const result = await analytics.track('Shopify Guides Setup Checked', { page_path: location.pathname }).promise;
      if (result?.code !== 200) throw new Error('App analytics setup check was not accepted.');
      if (!allowed() || stopped) return;
      if (window.engagement) throw new Error('An engagement installation already exists; merge it.');
      const host = config.serverZone === 'EU' ? 'https://cdn.eu.amplitude.com' : 'https://cdn.amplitude.com';
      await new Promise((resolve, reject) => {
        const script = document.createElement('script');
        const timeout = setTimeout(() => reject(new Error('Engagement script timed out. Reload before retrying.')), 15000);
        script.src = host + '/script/' + encodeURIComponent(config.apiKey) + '.engagement.js';
        script.onload = () => { clearTimeout(timeout); resolve(); };
        script.onerror = () => { clearTimeout(timeout); reject(new Error('Engagement script failed. Reload before retrying.')); };
        document.head.appendChild(script);
      });
      if (!allowed() || stopped) return;
      attached = true;
      await analytics.add(window.engagement.plugin({ serverZone: config.serverZone })).promise;
      if (!allowed() || stopped) window.engagement.shutdown();
    })();
    window.ampShopifyGuidesReady = starting;
    return starting;
  }
  const update = () => {
    if (!allowed()) {
      if (permissionSeen) {
        stopped = true;
        if (attached) window.engagement?.shutdown();
      }
      return;
    }
    permissionSeen = true;
    start().catch(error => console.warn('Shopify guides setup:', error.message));
  };
  document.addEventListener('visitorConsentCollected', update);
  window.Shopify?.loadFeatures?.([{ name: 'consent-tracking-api', version: '0.2' }], error => {
    if (!error) update();
  });
})();
```

Create `snippets/amp-shopify-guides.liquid`. Replace the key with the app-connected public key and set serverZone to EU for an EU project. The JavaScript selects the matching engagement CDN host.

snippets/amp-shopify-guides.liquid

```liquid
{% comment %} snippets/amp-shopify-guides.liquid: preserve the app installation and other theme scripts. {% endcomment %}
{% assign amp_build_api_key = 'AMPLITUDE_API_KEY' %}
<script type="application/json" id="amp-shopify-config">
  { "apiKey": {{ amp_build_api_key | json }}, "serverZone": "US" }
</script>
<script src="{{ 'amp-shopify-guides.js' | asset_url }}" defer></script>
```

Render the snippet once near the closing body in `layout/theme.liquid`, preserving every existing script and the app's installation.

layout/theme.liquid

```liquid
{% comment %} layout/theme.liquid: add near closing body; keep offer events and experiment consent. {% endcomment %}
{% render 'amp-shopify-guides' %}
```

Shopify's Customer Privacy API gates this addition. The plugin manages initialization and boot; do not call manual engagement.init or boot. On withdrawal after permission was granted, the code stops new setup, calls shutdown if attached, and requires a reload before a later re-grant can restart it. This does not configure consent for the app's own collection. Verify both on the hosted store.

## 3. Save a survey for the actual offer page

In Guides and Surveys, create a web survey with a modal, a required List question, and a completion action or step. Save it before starting Preview. Choose Immediately for this first test and scope Where to the storefront URL containing the offer. Use a test audience or Testing status rather than exposing the survey to ordinary shoppers.

Survey builder configuration

```text
# Survey builder configuration
Title: Shopify offer feedback QA
Question: What would help you choose this product?
Choices: Size details; Delivery details; Product details
Trigger: Immediately
Where: actual storefront offer page
Audience: synthetic test visitor only
```

Retain data-guide="shopify-offer" on the offer if you later add an anchored guide. The modal survey needs no anchor. An optional Shopify Offer Clicked trigger must occur after ampShopifyGuidesReady resolves; events from before plugin attachment are not replayed into it.

## 4. Preview and verify the stored answer

Use the documented Preview flow on the actual storefront page. Keep Don't automatically close the preview window enabled for its cross-window connection, especially while the SDK loads. Wait for the addition to finish, complete the survey with a synthetic choice, and inspect the survey's stored response and test timestamp. Turn off Ignore test/preview data in the reporting filter and include the test time range.

Browser Console on a consented storefront test visit

```javascript
// Browser Console on a consented storefront test visit
await window.ampShopifyGuidesReady;
window.amplitude.getDeviceId();
window.amplitude.flush();
```

If the survey does not display, check page targeting, trigger, user eligibility, limits, and throttle separately from SDK load. Verify saved Responses before calling the test complete. A resolved setup promise or boot event is not a stored answer. Test one page first; don't depend on a multi-page preview to establish this result.

**MCP:** Ask your coding agent to inspect the project's event taxonomy with the [Amplitude MCP server](https://amplitude.com/docs/amplitude-ai/amplitude-mcp) before adding custom events. Reuse an existing name when it describes the same action.

## Verify it worked

1. **Storefront:** Open the actual storefront URL while logged out. Inspect the app's script and any theme additions. Use one app-owned analytics installation; a pixel sandbox and the top storefront window are different contexts.
2. **Delivery:** In browser Network, confirm event POSTs return HTTP 200 from `api2.amplitude.com` for US or `api.eu.amplitude.com` for EU. Compressed requests are not a reliable place to read event contents.
3. **Instrumentation Explorer:** Inspect event names, properties, counts, and `device_id` in the Chrome extension. Compare one action with one expected event.
4. **User Profiles:** Search that device ID in Users > User Profiles and inspect its event stream. The docs describe ten seconds to one minute for events. Ingestion Debugger provides request and event counts, not individual payloads.

Verify a saved synthetic survey answer separately from SDK boot and configuration.

## Troubleshooting

**The survey will not save**

Record the product error and resolve persistence before Preview. Repeated save attempts are not a successful saved survey.

**Guides stop after permission changes**

After withdrawal, reload the storefront before re-granting for a fresh test. The code deliberately does not manually boot a plugin that was shut down.

**Nothing appears after connecting the app**

Check the connected project key, storefront privacy decision, app installation, script requests, and browser blockers. Use the storefront's top window when checking `window.amplitude`; a sandboxed custom pixel is a separate environment.

**Events appear twice**

Look for a second SDK in a custom pixel, tag manager, theme snippet, or older integration. Remove the duplicate installation through its supported configuration. Keep the app-owned SDK and the existing theme code.

**Theme editing changes the result**

Test the actual storefront URL as well as the duplicated theme preview. Confirm the same product, template, hostname, and privacy state. Theme editor rendering alone is not a hosted-store verification.

## FAQ

### Why does this wait for a setup event before adding engagement?

The app controls analytics startup. The existing SDK's completed track call gives this addition a sequencing point without calling init again. It is a synthetic diagnostic, not a survey response.

### Can I use the Shopify pixel sandbox for an anchored guide?

Keep this file in the storefront theme. Pixel sandboxes are separate contexts and a selector cannot cross an iframe boundary to the storefront element.

## Full code reference

Unchanged from earlier pages; keep your versions: App installation, sections/amplitude-build-offer.liquid, assets/amp-shopify-events.js, assets/amp-shopify-experiment-consent.js.

assets/amp-shopify-guides.js

```javascript
// assets/amp-shopify-guides.js
// Preserve the app-owned SDK/init, amp-shopify-events.js, replay privacy, and experiment target.
(() => {
  if (window.__ampShopifyGuides) return;
  window.__ampShopifyGuides = true;
  const config = JSON.parse(document.getElementById('amp-shopify-config').textContent);
  const allowed = () => window.Shopify?.customerPrivacy?.analyticsProcessingAllowed() === true;
  let starting;
  let attached = false;
  let stopped = false;
  let permissionSeen = false;
  const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
  async function start() {
    if (!allowed() || stopped) return;
    if (starting) return starting;
    starting = (async () => {
      const deadline = Date.now() + 15000;
      while (typeof window.amplitude?.track !== 'function' ||
             typeof window.amplitude?.add !== 'function' ||
             !window.amplitude?.getDeviceId?.()) {
        if (!allowed()) return;
        if (Date.now() >= deadline) throw new Error('App-owned Browser SDK 2 is not ready.');
        await wait(100);
      }
      const analytics = window.amplitude;
      // A completed track call waits for the existing SDK pipeline. Never reinitialize it.
      const result = await analytics.track('Shopify Guides Setup Checked', { page_path: location.pathname }).promise;
      if (result?.code !== 200) throw new Error('App analytics setup check was not accepted.');
      if (!allowed() || stopped) return;
      if (window.engagement) throw new Error('An engagement installation already exists; merge it.');
      const host = config.serverZone === 'EU' ? 'https://cdn.eu.amplitude.com' : 'https://cdn.amplitude.com';
      await new Promise((resolve, reject) => {
        const script = document.createElement('script');
        const timeout = setTimeout(() => reject(new Error('Engagement script timed out. Reload before retrying.')), 15000);
        script.src = host + '/script/' + encodeURIComponent(config.apiKey) + '.engagement.js';
        script.onload = () => { clearTimeout(timeout); resolve(); };
        script.onerror = () => { clearTimeout(timeout); reject(new Error('Engagement script failed. Reload before retrying.')); };
        document.head.appendChild(script);
      });
      if (!allowed() || stopped) return;
      attached = true;
      await analytics.add(window.engagement.plugin({ serverZone: config.serverZone })).promise;
      if (!allowed() || stopped) window.engagement.shutdown();
    })();
    window.ampShopifyGuidesReady = starting;
    return starting;
  }
  const update = () => {
    if (!allowed()) {
      if (permissionSeen) {
        stopped = true;
        if (attached) window.engagement?.shutdown();
      }
      return;
    }
    permissionSeen = true;
    start().catch(error => console.warn('Shopify guides setup:', error.message));
  };
  document.addEventListener('visitorConsentCollected', update);
  window.Shopify?.loadFeatures?.([{ name: 'consent-tracking-api', version: '0.2' }], error => {
    if (!error) update();
  });
})();
```

snippets/amp-shopify-guides.liquid

```liquid
{% comment %} snippets/amp-shopify-guides.liquid: preserve the app installation and other theme scripts. {% endcomment %}
{% assign amp_build_api_key = 'AMPLITUDE_API_KEY' %}
<script type="application/json" id="amp-shopify-config">
  { "apiKey": {{ amp_build_api_key | json }}, "serverZone": "US" }
</script>
<script src="{{ 'amp-shopify-guides.js' | asset_url }}" defer></script>
```

## Hand it to your agent

Give your agent this tutorial and the existing theme files. Keep the app installation, all sibling theme additions, and the selected test product.

Agent prompt

```text
Agent task: Shopify guides & surveys.
Use the official Amplitude Shopify app as installation owner. Merge only this page's additions; preserve content_for_header, product/cart templates, replay privacy, offer attributes, custom event handlers, and consent. Confirm actual Browser SDK methods before using snippets. Test consent and the promised outcome on a real Shopify test store. Report SDK delivery, native Shopify behavior, product configuration, and processed results separately. Do not call another analytics init or claim checkout behavior from a storefront test.
```

## What this unlocks

[Product analytics](https://amplitude.com/amplitude-analytics) compares Shopify Offer Clicked with the app's observed product and cart events after both are verified.

[Session replay](https://amplitude.com/session-replay) shows the storefront interaction after capture, identity, and privacy checks pass.

[Web Experiment](https://amplitude.com/web-experimentation) uses the observed Shopify Offer Clicked event as the offer-copy test's primary metric.

[Guides and surveys](https://amplitude.com/guides-and-surveys) can ask what product information a visitor needs after the separate SDK and targeting are configured.

## Next steps

[ShopifyHow to add web analytics to a Shopify storeConnect Shopify to Amplitude with the official app. Verify storefront page views, campaign attribution, and anonymous visitor continuity.](/build/shopify-web-analytics)[ShopifyHow to add product analytics to a Shopify storeTrack a Shopify offer click alongside app-provided product and cart events. Keep clicks separate from confirmed commerce outcomes.](/build/shopify-product-analytics)[ShopifyHow to add session replay to a Shopify storeConfigure Shopify replay sampling and masking through the app-connected project. Verify a synthetic storefront recording before production capture.](/build/shopify-session-replay)

Reference docs

[Amplitude shopify plugin →](https://amplitude.com/docs/data/amplitude-shopify-plugin)[Sdk →](https://amplitude.com/docs/sdks/guides-and-surveys/sdk)[Build a survey →](https://amplitude.com/docs/guides-and-surveys/build-a-survey)[Setup and target →](https://amplitude.com/docs/guides-and-surveys/setup-and-target)[Testing →](https://amplitude.com/docs/guides-and-surveys/testing)[Shopify Customer Privacy API →](https://shopify.dev/docs/api/customer-privacy)

For agents

[View raw .md](/build/shopify-guides-surveys.md)[llms.txt](/llms.txt)

## Add Amplitude to your Shopify site

Connect the official Shopify app and follow the tutorials for each product.

[Create a free account](https://app.amplitude.com/signup)[Browse all Build tutorials](https://amplitude.com/build)
