Type something to search...
Advanced Shopify Webhook Tracking

Part II: Google Ads, Server-Side GTM & Stape Store

Advanced Google Ads Tracking for Shopify with Stape & Webhooks

TutorialsIvanPublished: 04 Nov, 2026

Even with a solid Server-Side Google Tag Manager (sGTM) setup running on Stape.io, relying strictly on browser-triggered purchase events still leaves conversion gaps. When shoppers pay through external redirects (Shop Pay, PayPal, Klarna, 3D Secure), use aggressive ad blockers, or close the browser tab before the Thank You page loads, the browser event either fails to fire at all or lands on the confirmation page stripped of its Google Ads click cookie (_gcl_aw).

In this advanced guide to Shopify & Stape conversion tracking, we take our standard server-side setup a step further by ingesting Shopify Order Webhooks directly into Server GTM.

The Inspiration & Why We Skip Transaction Filtering for Google Ads

Recently, Stape shared a hybrid webhook architecture (demonstrated with Meta CAPI) that uses Stape Store for two things:

  1. Saving browser click cookies at checkout start keyed by the Shopify cart_token (since backend Shopify webhooks never carry browser cookies).
  2. Logging completed browser orders by transaction_id and delaying webhooks by 10 minutes so the webhook only fires when the browser tag fails.

While filtering out webhooks is necessary in standard Meta setups (where browser tags generate a random event_id per page load), Google Ads natively deduplicates conversions by Transaction ID within the same conversion action — and merges browser and server signals when they share the same Transaction ID.

In our Shopify Webhook Tracking Case Study across 1,000+ monthly orders, we found that sending unfiltered, cookie-enriched webhooks into the exact same Primary Website conversion action as our browser pixel:

  • Recovers orders where the Thank You page loaded without the _gcl_aw cookie (which a filtered setup blocks).
  • Eliminates the need for Stape’s Request Delay power-up (meaning this setup works on Stape’s $20/mo Pro plan rather than requiring a $100+/mo Business plan).
  • Reduces Stape Store operations.

Let’s build this pipeline step by step.


High-Level Architecture Overview

Here is the data flow we will implement. At checkout start, a lightweight Shopify Web Pixel saves the visitor’s _gcl_aw cookie and cart_token into Stape Store. When the order completes, both the browser purchase tag and the enriched Shopify webhook report to the same Google Ads Website conversion action with the exact same Transaction ID:

flowchart LR
    A[1. Checkout Start] -->|Save _gcl_aw + cart_token| B[(Stape Store)]
    C[2a. Browser Purchase] -->|Web Tag| E((Google Ads<br/>Single Action))
    D[2b. Shopify Webhook] -->|Lookup GCLID| B
    B -->|Enriched Webhook Tag| E

    classDef step fill:#E8F4FD,stroke:#1C7ED6,stroke-width:2px,color:#1864AB;
    classDef store fill:#F3F0FF,stroke:#7950F2,stroke-width:2px,color:#5F3DC4;
    classDef google fill:#E6FCF5,stroke:#0CA678,stroke-width:2px,color:#065F46;

    class A,C,D step;
    class B store;
    class E google;

Prerequisites

Before we start, let’s make sure we have:

  1. A Shopify store with access to Settings > Customer Events and Webhooks.
  2. A Server Google Tag Manager (sGTM) container hosted on Stape.io with a custom domain (e.g., https://x.yourdomain.com) and standard web purchase tracking already in place. (Note: the subdomain doesn’t need to be a single letter like x — it can be whatever word or string we decide, as long as it doesn’t signal that it is used for tracking like gtm, track, or analytics.)
  3. The Stape Store feature enabled in our Stape container (included on all Stape plans, including Pro).
  4. A Google Ads account with a Primary Website Purchase conversion action.

Step 1: Connect Stape to Google Ads (Google Data Manager API)

To send server-to-server webhook conversions from sGTM to Google Ads and have them natively deduplicate against our Website pixel, we use the Google Data Manager API tag authenticated via Stape Connections.

1.1 Authorize the Google Ads Connection in Stape

  1. Log into the Stape.io dashboard and open the sGTM container.
  2. In the left sidebar, click Connections.
  3. Find Google Data Manager API and click Connect.
  4. Log in with the account that has access to the Google Ads account.
  5. Click Add account and link the Google Ads account ID. If you used Google Account that is tied to MCC Google Ads, you’ll need MCC account ID too.

1.2 Note the Website Conversion Action ID (ctId)

Instead of creating a separate “Import from clicks” offline conversion action, we will send webhook events directly to our existing Primary Website Purchase conversion action:

  1. In Google Ads, go to Goals > Conversions > Summary.
  2. Click on the primary Website purchase conversion action.
  3. Look at the browser URL and copy the number after ctId= (for example, 7419377640). This is our Conversion Action ID / Destination ID.
  4. Also note the 10-digit Google Ads Customer ID (for example, 169-648-0016).

Step 2: Install the “Data Client” in Server GTM

By default, a Server GTM container only includes the GA4 Client, which only listens to /g/collect. When a Shopify webhook or custom JSON request hits our server container, sGTM will reject it with HTTP 400: No client claimed the request unless we install Stape’s Data Client.

2.1 Import & Create the Data Client

Unlike tag and variable templates, Client templates are not listed in the GTM Community Template Gallery, so we need to download the template file first:

  1. Go to the Stape Data Client GitHub repository and download data-client.tpl (if we download the full repository as a ZIP file, unpack it first — only the data-client.tpl file needs to be uploaded to GTM).
  2. In Server GTM, go to Templates > Client Templates > New.
  3. Click the three dots in the top-right corner, click Import, select the data-client.tpl file, and click Save.
  4. Go to Clients > New, choose Data Client, and name it Data Client.
  5. Under Accepted Path Settings, add two paths:
    • /data
    • /webhooks
  6. Click Save.

Tip

Why add /webhooks alongside /data?


If we enable the Stape Bot Detection power-up to stop bots from burning our monthly sGTM request quota, Stape inspects incoming requests to standard endpoints like /g/collect and /data.



• Shopify Customer Events (/data): Runs directly inside the shopper’s browser at checkout start, carrying a real residential IP and browser fingerprint. Keeping Customer Events on /data is ideal because Bot Detection will block actual checkout-scraping bots from firing store_cookies and burning our Stape Store quota.


• Shopify Order Webhooks (/webhooks): Fired server-to-server from Shopify’s backend cloud IPs with no browser fingerprint. Because Bot Detection on /data can mistake backend webhook POST requests for datacenter bots and block them with 403 Forbidden, pointing Shopify Webhooks to a dedicated /webhooks path ensures they always pass through.


Step 3: Capture Google Ads GCLID (_gcl_aw) & cart_token in Shopify Customer Events

Because Shopify webhooks fire from Shopify’s backend servers without cookies, we use a Shopify Custom Web Pixel during checkout initiation to grab the visitor’s cart_token and Google Ads click cookie (_gcl_aw), sending both from the browser to /data.

  1. In Shopify Admin, go to Settings > Customer Events.
  2. Click Add custom pixel and name it Stape Cookie & Cart Sync.
  3. Configure Customer Privacy according to the store’s consent policy.
  4. Paste the following code (replacing https://x.yourdomain.com/data with our sGTM domain):
// Shopify Custom Web Pixel: Server GTM Cookie & Cart Sync
analytics.subscribe("checkout_started", (event) => {
  setTimeout(async () => {
    try {
      // Extract the unique Shopify cart identifier
      const cartToken =
        init.data?.cart?.id || (await browser.cookie.get("cart"));
      if (!cartToken) return;

      // Retrieve Google Ads click cookie (_gcl_aw)
      let gclAw = "";
      try {
        gclAw = await browser.cookie.get("_gcl_aw");
      } catch (e) {
        // Cookie not present or blocked
      }

      // Optional: Also capture Meta cookies (_fbp, _fbc) if running Meta CAPI webhooks
      let fbp = "";
      let fbc = "";
      try {
        fbp = await browser.cookie.get("_fbp");
        fbc = await browser.cookie.get("_fbc");
      } catch (e) {}

      // Only send request if at least one marketing cookie exists
      if (!gclAw && !fbp && !fbc) return;

      fetch("https://x.yourdomain.com/data", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          event: "store_cookies",
          cart_token: cartToken,
          gcl_aw: gclAw || "",
          fbp: fbp || "",
          fbc: fbc || "",
        }),
        keepalive: true,
      });
    } catch (err) {
      console.error("sGTM Cookie Sync Error:", err);
    }
  }, 1500);
});
  1. Click Save, then click Connect.

Note: If we already use the Stape Conversion Tracking app for Shopify, it can automatically send store_cookies events with cart_token and gcl_aw to our /data endpoint.


Step 4: Write _gcl_aw to Stape Store (With a Quota-Saving Filter)

When the store_cookies event arrives in Server GTM, we write gcl_aw into Stape Store using cart_token as the document key.

4.1 Create Event Data Variables

In Server GTM, go to Variables > User-Defined Variables > New and create:

  • ed - cart_token: Variable Type Event Data -> Key Path: cart_token
  • ed - gcl_aw: Variable Type Event Data -> Key Path: gcl_aw

4.2 Create the Trigger (Only When a Click ID Exists)

Roughly 80–90% of site checkouts come from organic, direct, email, or returning visitors who do not have a _gcl_aw cookie. If we write to Stape Store on every single checkout, we will burn thousands of unnecessary Stape Store requests every month.

Go to Triggers > New:

  • Name: ce - store_cookies (has gcl_aw)
  • Trigger Type: Custom Event
  • Event Name: store_cookies
  • This trigger fires on: Some Custom Events
    • ed - gcl_aw matches RegEx GCL\.
    • ed - cart_token matches RegEx ^[A-Za-z0-9_-]{20,}$

(Alternatively, if we use a single Stape Store Writer tag for multiple platforms, we can add a blocking trigger when gcl_aw does not match GCL\..)

4.3 Activate Stape Store & Create the Stape Store Writer Tag

Warning

Don’t Forget: Activate Data Store in Your Stape Container First!


While existing containers may already have Stape Store turned on, new Stape containers have it disabled by default. Before testing the writer tag, open your container in the Stape.io dashboard, go to the Store tab in the top navigation, and click Activate Data Store for this container. If we skip this step, the Stape Store Writer tag will fail in sGTM Preview Mode without an obvious reason.

  1. In Server GTM Templates > Tag Templates, search the Community Template Gallery and import Stape Data Store Writer.
  2. Go to Tags > New:
    • Name: Stape Store Writer | Checkout Cookies
    • Tag Type: Stape Store Writer
    • Document ID: ed - cart_token
    • Stape Store Collection Name: checkout_cookies
    • Custom Data:
      • Key: gcl_aw -> Value: ed - gcl_aw
    • Firing Trigger: ce - store_cookies (has gcl_aw)
  3. Click Save.

Step 5: Configure the Shopify Order Webhook to /webhooks

Next, we configure Shopify to send order webhooks to our sGTM /webhooks endpoint.

  • Option A: Via the Stape Shopify App

    1. Open the Stape Conversion Tracking app in Shopify Admin.
    2. Go to the Webhooks tab and enable Send webhooks to sGTM container.
    3. Set the Webhook URL to:
      https://x.yourdomain.com/webhooks
    4. Enable Purchase webhook and click Save.
  • Option B: Via Native Shopify Webhooks

    1. Go to Shopify Admin > Settings > Notifications > Webhooks.
    2. Click Create webhook.
    3. Event: Select Order creation (or Order payment).
    4. Format: JSON.
    5. URL: https://x.yourdomain.com/webhooks
    6. Click Save.

Step 6: Extract the Stored GCLID & Map Webhook Data

When a Shopify order webhook hits /webhooks, the Data Client claims it and parses the payload into Server GTM Event Data (typically firing an event named purchase_webhook or purchase).

6.1 Lookup gcl_aw from Stape Store

  1. In Templates > Variable Templates, import the Stape Store Lookup template from the Community Gallery.
  2. Go to Variables > New:
    • Name: ss - gcl_aw by cart_token
    • Variable Type: Stape Data Store Lookup
    • Lookup Type: Document ID
    • Document ID: ed - cart_token (Ensure this matches the Event Data path where the webhook provides the cart token)
    • Stape Store Collection Name: checkout_cookies
    • Document Path: gcl_aw
    • Store Response in Cache: True

6.2 Extract the Pure gclid Using Regex

The raw _gcl_aw cookie is stored in the format GCL.1712345678.Cj0KCQjwiMmwBh… The Google Data Manager API requires only the pure Click ID after the second dot.

Create a Regex Table variable:

  • Name: regex - extracted gclid from gcl_aw
  • Variable Type: Regex Table
  • Input Variable: ss - gcl_aw by cart_token
  • Row 1:
    • Pattern: ^GCL\.[0-9]+\.(.*)$
    • Output: $1
  • Default Value: Leave unchecked (returns undefined if no match).
  • Advanced Settings: Uncheck Full Matches Only if needed, and enable Capture Groups and Replace Functionality.

6.3 Map Customer & Order Variables

Warning

Watch Out: user_data.email_address vs user_data.email


While some legacy tags use user_data.email, Stape’s Data Client normalizes incoming Shopify webhook customer objects to Google’s Enhanced Conversions schema:


• user_data.email_address (NOT user_data.email)


• user_data.phone_number (NOT user_data.phone)



Always verify the exact key paths inside sGTM Preview Mode’s Event Data tab.

We create the following Event Data variables in Server GTM:

Variable NameTypeKey Path
ed - shopify webhook - transaction_idEvent Dataecommerce.transaction_id
ed - shopify webhook - valueEvent Dataecommerce.value
ed - shopify webhook - currencyEvent Dataecommerce.currency
ed - shopify webhook - itemsEvent Dataecommerce.items
ed - shopify webhook - email_addressEvent Datauser_data.email_address
ed - shopify webhook - phone_numberEvent Datauser_data.phone_number

6.4 Ensure Strict 1:1 Transaction ID Parity Between Web & Webhook

Before moving to the tag configuration, we must double-check what our Browser Purchase Tag sends as its Transaction ID vs. what our Shopify Webhook sends in ecommerce.transaction_id:

  • Depending on the Shopify pixel or app integration, one might send Shopify’s internal numeric Order ID (e.g., 5839201943812) while the other sends the order number (1042 or #1042).
  • They must be identical. If Transaction IDs do not match 1:1 between web and webhook, Google Ads cannot deduplicate those orders. Inspect a real order in sGTM Preview Mode across both the browser purchase event and the purchase_webhook event, and ensure both tags pass the exact same string.

Step 7: Configure the Google Data Manager API Tag in Server GTM

To implement our unified Web + Unfiltered Webhook architecture:

7.1 Existing Browser Purchase Tag

We keep our standard Server GTM Google Ads Conversion Tracking tag firing on our browser purchase trigger:

  • Conversion ID & Conversion Label: Pointing to our Primary Website Purchase conversion action.
  • Order ID (Transaction ID): Mapped to the Shopify Order ID (matching the webhook’s Transaction ID).
  • Enhanced Conversions: Enabled with user-provided data.

7.2 Unfiltered Webhook Conversion Tag

  1. In Server GTM Templates > Tag Templates, search the Community Gallery for Google Data Manager API Conversion Events (by Stape) and add it to the workspace.
  2. Go to Tags > New:
    • Name: Google Ads - Purchase Webhook (Unfiltered)
    • Tag Type: Google Data Manager API Conversion Events
    • Event Source: WEB (Setting WEB allows sending to our Website conversion action so Google Ads deduplicates against the browser pixel)
    • Operating Customer ID: Our 10-digit Google Ads Account ID, make sure to remove the dashes
    • Product Destination ID (ctId): Our Primary Website Purchase Conversion Action ID (from Step 1.2)
    • Transaction ID: ed - shopify webhook - transaction_id
    • Conversion Value: ed - shopify webhook - value
    • Currency Code: ed - shopify webhook - currency
    • Ad Identifiers -> GCLID: regex - extracted gclid from gcl_aw
    • User Data:
      • Email Addresses: ed - shopify webhook - email_address
      • Phone Numbers: ed - shopify webhook - phone_number
      • (Optional) Map Billing First Name, Last Name, Country, and Postal Code from user_data.address.
  3. Firing Trigger: Create a Custom Event trigger for purchase_webhook firing on All Custom Events (no Stape Store transaction filter required).
  4. Click Save.

Step 8: Testing & Verifying in Server GTM Preview Mode

Before publishing the container, we test both phases in sGTM Preview Mode:

  1. Test Cookie Storage at Checkout:

    • Open sGTM Preview Mode.
    • Visit the store in a fresh window with a test click ID: https://yourstore.com/?gclid=TestGclid123456789
    • Add an item to cart and proceed to Checkout.
    • In sGTM Preview, verify that store_cookies arrives via Data Client and Stape Store Writer | Checkout Cookies fires with status 200 OK.
    • In the Stape Dashboard > Store > checkout_cookies, confirm the document keyed by our cart_token contains gcl_aw.
  2. Test the Order & Webhook Execution:

    • Complete a test purchase.
    • Verify the browser purchase event fires our standard Google Ads Conversion Tracking tag with the Order ID (e.g., 5839201943812).
    • Seconds later, verify the purchase_webhook event arrives on /webhooks via Data Client.
    • Click on Google Ads - Purchase Webhook (Unfiltered) in Preview Mode and confirm:
      • Transaction ID is identical (5839201943812).
      • GCLID resolved to TestGclid123456789.
      • User Data populated email_address and phone_number.
      • Outgoing HTTP Response: Returns 202 Accepted (Google’s Data Manager API returns 202 Accepted rather than 200 OK when a conversion payload is authenticated and queued for attribution).

Bonus Appendix: When & How to Use the “Filtered Webhook” Pattern Instead

While sending unfiltered webhooks into the same Website conversion action is simpler, uses fewer Stape Store requests, works without the Business-plan Request Delay power-up, and rescues “cookie-less Thank You page” conversions, when might we still use the Filtered Webhook pattern?

We might choose the Filtered Backup pattern if:

  • We want to send webhook conversions to a separate conversion action (such as an Import from clicks action) without double-counting against our primary Website action.
  • We are using the exact same webhook trigger to fire Meta CAPI, TikTok, or GA4 tags where deduplication rules differ.

If we want to add the Filtered Backup gate (and we have a Stape Business plan with the Request Delay power-up enabled):

  1. Enable Request Delay in Stape: Turn on the Request Delay power-up in Stape and change our Shopify Webhook URL to:
    https://x.yourdomain.com/stape/delay/10/webhooks
    Why? Shopify dispatches webhooks within 1–2 seconds of payment, whereas the customer’s browser often takes 5–30 seconds to redirect from PayPal or Shop Pay and load the Thank You page. Without a 10-minute delay, the webhook checks Stape Store before the browser writes to it, causing a race condition.
  2. Write Web Purchases to Stape Store: Create a Stape Store Writer tag (Stape Store Writer | Web purchase) firing on our browser purchase trigger that writes source: “web” into a transactions collection using ed - transaction_id as the Document ID.
  3. Lookup Web Purchases on Webhook Arrival: Create a Stape Store Lookup variable (ss - web purchase lookup) that queries the transactions collection for ed - shopify webhook - transaction_id at path source (with Store Response in Cache set to False).
  4. Gate the Webhook Trigger: Update our purchase_webhook trigger so it only fires when ss - web purchase lookup does not equal web.

Frequently Asked Questions

Why don’t Shopify webhooks include the Google Ads GCLID (_gcl_aw) by default?

Shopify order webhooks are dispatched server-to-server from Shopify’s backend infrastructure when an order is created or paid. Because cookies like \_gcl_aw live exclusively in the shopper’s browser, backend webhooks do not have access to browser cookies unless we capture \_gcl_aw during checkout (via a Shopify Custom Web Pixel) and store it against the cart_token in a key-value store like Stape Store.

Does Google Ads deduplicate browser purchase pixels and Shopify webhooks?

Yes — as long as both tags report into the same Primary Website conversion action (with the webhook tag’s Event Source set to WEB) and pass the exact same Transaction ID (Order ID). Google Ads natively deduplicates events sharing a Transaction ID within a single conversion action and merges Enhanced Conversions user data and click identifiers.

Why does the Google Data Manager API return HTTP 202 Accepted instead of 200 OK?

Unlike standard pixel endpoints that return 200 OK, Google’s Data Manager API queues incoming conversion events for asynchronous validation and attribution processing. An HTTP 202 Accepted response in Server GTM Preview Mode confirms that our payload passed authentication and schema checks and was accepted by Google Ads.

(Running custom payment gateways or CRM pipelines outside of Shopify and Stape? Check out our guides on Google Ads Offline Conversions via n8n and Adyen to Google Ads Offline Conversions.)

Need bulletproof Shopify & Google Ads tracking?

We build and audit enterprise Server-Side GTM pipelines that recover lost conversions without double-counting. Let's fix your attribution.

Book a Discovery Call

Related Posts

Adyen Purchase TrackingGoogle Ads Offline Conversions

Adyen to Google Ads Offline Conversions via n8n

TutorialsIvanPublished: 08 Oct, 2026

When running paid acquisition campaigns, relying solely on client-side conversion tags (like Google Tag Manager scripts on a checkout confirmation page) leaves costly blind spots. Browser privacy rest

read more
LTV based nCACAdvanced Google Ads bidding strategy

nCAC Google Ads Bidding Strategy based on LTV

TutorialsIvanPublished: 29 Sep, 2026

Your Google Ads account might be pausing your most profitable products right now — and the algorithm thinks it's doing you a favor. Lots of Google Ads managers live and die by immediate, dashboard-re

read more
n8n to Google AdsOffline Conversions

Google Ads Offline Conversions via n8n

TutorialsIvanUpdated: 23 Apr, 2026

If you are running lead generation campaigns, Offline Conversion Tracking is non-negotiable. It’s the only way to tell Google, "Hey, this lead actually bought something." While tools like Zapie

read more
Google Ads POAS TrackingShopify, Server-Side GTM, Stape & Matrixify

Shopify POAS Tracking for Google Ads via Stape

TutorialsIvanPublished: 23 Aug, 2026

As e-commerce advertising becomes more competitive, optimizing campaigns for ROAS (Return on Ad Spend) is no longer enough. High revenue doesn't always equal high profit, especially if your best-selli

read more
Google Ads Editor on LinuxA step-by-step guide to installing and running Google Ads Editor with Bottles and Wine

How to Run Google Ads Editor on Linux using Bottles

TutorialsIvanPublished: 15 Oct, 2026

For PPC specialists, media buyers, and agency professionals working on a Linux desktop, Google Ads Editor has long been a missing piece. Google officially provides desktop installers only for Windows

read more
Boost Your ROAS with AlgoShop AITurn hesitant ad clicks into profitable customers with proactive conversational commerce.

Boost Your ROAS with AlgoShop AI Chatbot for Shopify

TutorialsIvanPublished: 04 Oct, 2026

We talk a lot on this blog about optimizing Google Ads and tightening up conversion tracking. But here is a harsh truth for e-commerce brands: you can have the perfect campaign structure, the most ref

read more