Type something to search...
Google Ads POAS Tracking

Shopify, Server-Side GTM, Stape & Matrixify

Guide to Shopify POAS Tracking for Google Ads: Server-Side GTM, Stape, and Matrixify

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-selling items have razor-thin margins. The solution is POAS (Profit on Ad Spend) — feeding real-time profit margins directly into the Google Ads bidding algorithm.

Setting up a reliable, automated POAS pipeline for a large Shopify catalog can be incredibly complex. In this guide, we’ll walk through an enterprise-tier architecture using Shopify, Matrixify, Google Apps Script, Stape.io, and Server-Side GTM (sGTM) to seamlessly pass profit data to Google Ads via both web client tags and server-side webhooks.

The Tech Stack Overview

Here is how the data flows in this architecture:

  1. Matrixify Shopify app: Exports a daily CSV of your Shopify catalog (costs and prices).
  2. Google Apps Script: Ingests the CSV, formats the IDs, calculates true profit margins, and outputs the data to a Google Sheet.
  3. Google Sheets: Publishes a live CSV feed to the web.
  4. Stape POAS Power-Up: Reads the live Google Sheet and stores the margins in an edge database.
  5. Server-Side GTM: Formats incoming purchase payloads (web and webhooks), queries Stape for the margin, calculates the exact dollar profit, and sends it to Google Ads.

Let’s build it step-by-step.


Step 1: Automating Catalog Exports with Matrixify

For stores with thousands of SKUs, querying Shopify’s GraphQL API for costs on every purchase is too slow. Instead, we use the Matrixify app to run a scheduled daily export of our catalog.

The “Static URL” Trick

When you schedule a Matrixify export to a custom server or URL, the app automatically appends dynamic parameters to the end of the file link (e.g., ?job=707836997&kind=exported).

To make this predictable for an automated script:

  1. In Matrixify, under Options, select Custom File Name (e.g., stape_poas_feed.csv).
  2. Uncheck Zip CSV files.
  3. Take the resulting URL and simply delete everything after .csv. Matrixify’s servers will automatically route any request to that clean, static URL to the most recently generated file.

Step 2: Processing the Data with Google Apps Script

Google Ads requires product IDs to match the format inside Google Merchant Center (usually shopify_US_{product_id}_{variant_id}). Furthermore, we need to calculate our margins and account for fixed overheads.

It’s also a good practice to set up fallback values.

We can use a Google Apps Script for this. Create a Google Sheet, open Extensions > Apps Script, and paste the app script code.

You can quickly use AI to get the code that processes the data, or reach out and I will share my latest and battle-tested snippet.

Crucial Gotcha: If you are passing percentage margins to Stape, they must be formatted as whole numbers (e.g., 20 for 20%), not decimals (0.2). If you pass 0.2, Stape will evaluate it as 0.2%, resulting in $0 profit calculations.

Set up a daily time-driven trigger in Apps Script to run this function after your Matrixify export finishes.


Step 3: Publishing to the Web for Stape

Stape needs access to this data. In your Google Sheet:

  1. Go to File > Share > Publish to web.
  2. Select your specific tab and change the format from “Web page” to Comma-separated values (.csv).
  3. Under Published content & settings, ensure “Automatically republish when changes are made” is checked.
  4. Copy the generated URL.

Step 4: Configuring the Stape POAS Power-Up

  1. Log into your Stape.io dashboard and open your Server container.
  2. Go to Power-ups > POAS Data Feed.
  3. Paste your published Google Sheets CSV URL.
  4. Under Column Mapping:
    • Item ID: id
    • Value: margin
    • Value Type: Choose whichever type you opted for

Step 5: Formatting the Data in Server-Side GTM

When a purchase payload hits Server-Side GTM, it comes in two distinct formats:

  • Web Purchases (GA4 Client): Uses item_id, item_variant, price, etc.
  • Webhook Purchases (Data Manager API): Often uses raw Shopify keys like product_id, variant_id, and line_items.

The Sandboxed JS Trap

sGTM runs in a sandboxed JavaScript environment. If your web or webhook payload passes price or quantity as a string (e.g., "49.99"), Stape’s margin calculator will attempt to multiply a string by a number, resulting in NaN and outputting $0.00 profit.

To fix this, create a Custom Variable Template in sGTM to universally standardize IDs for both web and webhooks, filter out shipping insurance, and force prices into numeric formats

Create a new Variable in sGTM using this template, and feed your Event Data - items (or line_items) into it. Name it something like {{ss - formatted items array}}.


Step 6: Setting up the Gross Profit Lookup Variable

With your items perfectly formatted, create a new variable using the Stape Store Margin Lookup template from the gallery.

  • Items Array Source: Custom items array
  • Custom Items Array: {{ss - formatted items array}}
  • Item ID Key Name: item_id
  • Item Price Key Name: price
  • Item Quantity Key Name: quantity

Testing Warning: During testing, uncheck “Store each Item Margin in cache”. If you make a mistake and the variable calculates a $0 profit, Stape will cache that $0 result for 180 minutes, making it seem like your fixes aren’t working! Once verified, turn the cache back on.


Step 7: Routing Web and Webhook Conversions

Because our Custom JS Template normalizes both GA4 web events and Shopify webhooks into the exact same structure, we can use the exact same Stape Store Margin Lookup variable for both tag triggers.

Create two distinct tags in sGTM:

  1. Google Ads Conversion Tracking (Web): Triggers on the GA4 purchase event.
  2. Google Data Manager API Conversion Events (Webhook): Triggers on your Shopify purchase_webhook.

Set the Conversion Value on both tags to your {{ss - gross profit lookup}} variable. Make sure both tags pass the same transaction_id. Google Ads will receive both events, natively deduplicate them using the transaction ID, and register the exact dollar-amount profit of the order.


Conclusion

By unifying your data structure before it hits the lookup variable, ensuring your prices are strict numeric data types, and scaling your percentage margins to whole numbers, you can build a bulletproof POAS pipeline. Your Google Ads account will now bid based on actual bottom-line profitability, giving you a massive advantage over competitors still optimizing for gross revenue.

Need help setting this up?

We can help you build and maintain a bulletproof Server-Side GTM pipeline to optimize your campaigns for maximum bottom-line profit.

Book a Discovery Call

Related Posts

n8n to Google AdsOffline Conversions

How to Send Offline Conversions to Google Ads with n8n (HTTP Request Guide)

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