Google Ads conversions, uploaded from the server

Checkout has moved off Shopify, so Sable reports purchases to Google Ads itself: click ids captured in a first-party cookie, conversions uploaded through the Data Manager API, de-duplicated with the thank-you page tag.

Why Shopify's own tracking stops seeing purchases

Shopify's Google channel reports purchases from Shopify's checkout. Once the checkout runs on Sable, that page is never loaded, so the conversion has to be reported by Sable instead. Two things do it: a server-side upload keyed on the click id, and an optional tag on the thank-you page keyed on the same order id.

How the click is captured

The theme snippet runs on every storefront page. When the URL carries a click id or a campaign parameter it merges them into the _wc_attr cookie on your root domain, valid for 90 days and mirrored in localStorage in case cookies are cleared:

  • Click ids: gclid, gbraid, wbraid (Google), fbclid (Meta), ttclid (TikTok), msclkid (Microsoft).
  • Campaign parameters: utm_source, utm_medium, utm_campaign, utm_content, utm_term.
  • The landing page and the referrer.

When the shopper clicks Check out, the snippet sends the cookie with the hand-off and it is stored on the order. Nothing about the shopper's identity is read on the storefront; the cookie only holds the parameters above.

What is uploaded, and when

When Whop reports payment.succeeded, the order is created in Shopify and a google_click_upload job is queued. Jobs run every five minutes on a schedule, with retries and backoff when Google is slow, and each one is listed on the store's Tracking page with its attempts and outcome.

The upload goes to Google's Data Manager API (events:ingest), the path Google requires for new click-conversion integrations. One event per order:

  • One click id: gclid if present, else gbraid, else wbraid. Orders without any Google click id are skipped, and the job says so.
  • The order id as the transaction id. Google de-duplicates on it, so a retry or a second upload of the same order never counts twice.
  • The order total and currency, and the time it was paid, in the store's timezone.
  • The buyer's email and phone, hashed with SHA-256 after normalisation, as first-party identifiers. The plain values never leave Sable.

The thank-you page tag

If you also want the conversion counted in the browser, enter the Google Ads tag id (AW-…) and the conversion label in Settings. The thank-you page then fires gtag with the same transaction id as the upload, and Google keeps one of the two. The Tracking page lists both rows so you can see what is reported from where.

Setting it up

Platform side, once

  • Google Cloud: enable the Data Manager API and the Google Ads API, create an OAuth client, and add the https://www.googleapis.com/auth/datamanager scope under the consent screen's data access.
  • Platform environment: GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET and a GOOGLE_ADS_REFRESH_TOKEN minted with npm run google:oauth. A developer token is only used to list or create conversion actions, and GOOGLE_ADS_LOGIN_CUSTOMER_ID only when a manager account sits in between.

Per store, in Settings

  • Switch on Upload paid orders as click conversions.
  • Customer id: the Google Ads account, digits only.
  • Conversion action id: an upload-clicks conversion action. npm run google:conversions -- <customer id> lists them; npm run google:create-conversion -- <customer id> "<name>" creates one.
  • Optional: gtag id and gtag conversion label for the thank-you page.

Tracking then shows the store as Active, or tells you what is missing: ids on the store, or credentials in the platform environment.

Klaviyo, for completeness

The same job system sends Klaviyo a Started Checkout event the moment a buyer enters an email, carrying the cart and a link that resumes the checkout, so abandoned-cart flows keep working. Placed Order still arrives through Klaviyo's own Shopify integration, because the order exists in Shopify. Per store: switch it on in Settings and paste a private API key.

What is not there yet

  • Conversion adjustments on refunds: a refunded order stays counted in Google Ads for now.
  • Meta, TikTok and GA4 are fed by Sable's storefront pixel rather than by a server-side upload.

Questions people ask

Will a purchase be counted twice?

No. The upload and the thank-you page tag carry the same order id as the transaction id, which is what Google de-duplicates on. Retrying an upload is safe for the same reason.

What if the shopper came from an ad but the click id was lost?

Then there is nothing to upload and the job is skipped. The click id is captured on the first page the shopper lands on, so make sure the snippet is in the published theme and not only on the cart page.

Does it need Google's developer token?

Only to list or create conversion actions from the command line. The upload itself uses the Data Manager API with OAuth and no developer token.

Set it up on your own store

Connect Shopify, connect Whop, add one script tag. If it is not for you, removing the tag puts Shopify's checkout back.