The Furniture Brand Whose Revenue Showed Up as Unassigned
A high-ticket furniture store had a sophisticated server-side tracking stack on paper and a GA4 property that filed its revenue under Unassigned. The diagnosis found three separate failures stacked on top of each other. The fix was one checkbox, two surgical cuts, and one lowercase letter.
01 The numbers
- 1, from 2 competing GA4 installs on one property
- 0 Ecommerce events reaching the server stack, before
- 1 Characters changed to restore purchase reporting
- 0 New tools purchased
02 The breakdown
The Setup
A premium home furnishings brand on Shopify. Four-figure average order value, long consideration cycles, real ad spend. On paper, the tracking stack was sophisticated: a server-side Google Tag Manager container behind a first-party subdomain, GA4, Meta CAPI, and Microsoft Ads all wired through it.
In practice, GA4 filed the revenue under Unassigned. The owner could see orders in Shopify and money leaving the ad account, and no report that connected the two. The Google Ads campaign, starved of purchase signal, was optimizing toward add-to-carts instead of buyers.
What Was Broken
I audited the stack read-only before touching anything: the GTM containers through the API, the live storefront through a browser session that read the actual network beacons. Three failures, stacked so each one hid the next.
The server-side stack was sending nothing. Every server-side tag was triggered off ecommerce data layer events, and the Shopify app that should have pushed those events had its one load-bearing checkbox unchecked. No view_item, no add_to_cart, no purchase ever reached the server container. The expensive part of the stack was a bystander.
Two GA4 installs were fighting over one property. The intended path was the server-side container. But Shopify’s native Google channel app was also injecting its own client-side Google tag, configured to the same GA4 property. Two installs with different session handling, one property. GA4 could not resolve sessions to a channel, so revenue landed in Unassigned.
The web container loaded twice. Once from the first-party subdomain, once from a hardcoded legacy snippet in the theme. Same container, two loads, double-fire risk on every tag in it.
There was also a fourth, smaller defect waiting behind the others: the purchase tag sent its event name as capital-P Purchase. GA4 ecommerce binds to lowercase purchase. That one character was enough to keep purchases out of GA4’s ecommerce reports even when the event fired.
What I Did
Order mattered here. Lowercasing the purchase event while the container still loaded twice would have double-counted every order. So the sequence ran root-cause first:
- Turned on the data layer. One checkbox in the tracking app’s settings. Ecommerce events started flowing to the server container within minutes, verified as first-party requests returning 200.
- Cut the duplicate GA4 install. Disconnected the channel app’s Analytics connection only. Its Merchant Center feed and its client-side Ads conversions stayed live, because the Ads conversion migration had its own scheduled cutover and unplugging both at once would have blinded bidding.
- Removed the legacy container snippet from the theme via the Shopify CLI, byte-verified against a saved rollback copy. The container now loads once, first-party only.
- Published the lowercase purchase fix as its own container version, after the duplicate loads were confirmed gone.
Every step was verified at the wire, not the dashboard: one page_view beacon, one collection path, first-party only, and the live GA4 realtime report showing the session.
What I Did Not Claim
Attribution categorization is not retroactive. The months of Unassigned revenue stay Unassigned; the clean data starts at the fix. The channel mix takes one to two weeks to drain into the right buckets, and the Google Ads conversion cutover from add-to-carts to purchases follows its own verification window before bidding is allowed to retrain. There is no honest revenue-lift number to print yet, so none is printed here.
What is verified: the store went from two competing GA4 installs and a silent server stack to one clean, first-party collection path, confirmed beacon by beacon.
The Takeaway
The stack looked sophisticated because someone had bought the right architecture. It failed because nobody had watched the wire. Every layer reported healthy on its own dashboard while the layers disagreed with each other.
If your GA4 shows a pile of Unassigned revenue, do not start with attribution settings. Count your installs. Most stores I audit are running more than one and do not know it.
Wondering what your own stack sends? That is a one-day diagnosis.
03 More work
Other accounts, same approach.
gcs=G1-- (denied) consent state on beacons, before
A WooCommerce brand's visitors were accepting the cookie banner, and the Google beacons still fired with consent denied. The free tier of the consent platform never told Google about the yes. The ad account had been bidding on modeled data for users who had opted in. The fix was a small PHP snippet at the right priority.
Read the breakdown→
16 on-brand posts shipped
Built a multi-agent content engine for a Shopify home furnishings brand and ran it. Sixteen blog posts shipped as drafts, every one written in the brand voice, anchored to a real product, illustrated, and SEO-ready. No outside copywriter. No stock photos. No agency content retainer.
Read the breakdown→Ready to talk
Want this kind of breakdown on your account?
Thirty minutes on the phone. One clear set of next steps. Same person on the call as on the work.