We Cleaned Up a Client's GTM Mess and Found $340K They Didn't Know They Were Losing
Cleaned up client's GTM mess; found $340K they didn't know they were losing.

A CMO called us in March. "Our ROAS numbers in Facebook don't match our actual revenue. At all." We dug in. Turns out they had eight GTM containers across three domains, two abandoned by previous agencies, one firing duplicate events, and four with conflicting rules. The "working" tracking was double-counting conversions, missing cross-domain journeys, and firing Facebook Purchase events on random page loads.
This is more common than you'd think. Most companies accumulate GTM containers like technical debt. Each new agency, each new domain, each new platform gets its own container. Nobody audits. Nobody consolidates. And slowly, the data becomes fiction.
What We Actually Found
The container inventory was a graveyard:
| Container | Domain | Status | Issues |
|---|---|---|---|
| GTM-AAAA | [PROPERTY].com | Active | Duplicate GA4 + UA firing |
| GTM-BBBB | app.[PROPERTY].com | Active | Missing consent checks |
| GTM-CCCC | shop.[PROPERTY].com | Active | Facebook firing on all pageviews |
| GTM-DDDD | [PROPERTY].com | Abandoned | 47 tags, last edited 2022 |
| GTM-EEEE | blog.[PROPERTY].com | Active | No cross-domain linking |
| GTM-FFFF | [PROPERTY].co.uk | Active | Hardcoded old pixel IDs |
| GTM-GGGG | staging | Active | Mirroring prod (accident) |
| GTM-HHHH | legacy | Abandoned | 12 tags, broken triggers |
The abandoned containers were still loading. The staging container was firing to production pixels. The UK container was using a Facebook pixel from a previous agency that had been recycled to another client. We found 23 custom HTML tags. Three were loading third-party scripts the client didn't know about. Two were sending data to endpoints that returned 404s. One was loading a Facebook pixel via a redirect domain that had expired and was now parked.
Cross-domain tracking was completely broken. A user clicking a Google ad, reading a blog post, then purchasing on the shop domain was counted as three separate users. The purchase got attributed to "direct" or "organic" instead of the paid campaign that started it.
We estimated they were under-attributing paid channel revenue by about 35%. On $85K monthly ad spend, that meant roughly $340K in annual revenue was being misattributed or lost entirely.
What We Did
Phase 1: Audit and Document (16 hours)
We inventoried every tag, trigger, and variable. Every custom HTML tag. Every pixel ID, API key, and hardcoded credential. Every data layer variable and its source. Every consent state check (or lack thereof).
Phase 2: Clean Slate Container (24 hours)
We built a single new container with standardized data layer schema across all domains, consent-aware tag firing, server-side GTM routing for all marketing pixels, cross-domain measurement via GA4, and tag sequencing. The key decision was server-side GTM. Instead of loading Facebook, TikTok, and LinkedIn pixels in the browser, we send a single event to our sGTM endpoint and let it route to each platform. This reduced page weight by 40% and eliminated the pixel-blocking problem from iOS 14.5+ and Safari.
Phase 3: Migration and Testing (20 hours)
We ran parallel tracking for 14 days. New container fired alongside old ones, sending to test properties. We compared event counts, revenue attribution, page load times, and error rates.
Testing caught two issues before go-live: the shop domain's checkout button was using a deprecated data layer event name, and the blog's newsletter signup form was submitting before the consent state updated. Both would have broken conversion tracking on day one.
Phase 4: Cleanup and Handoff (12 hours)
Removed old containers, updated documentation, trained the internal team. Handoff included container change log, tag firing rules in plain English, testing checklist, and escalation path.
Total: 72 hours over 4 weeks.
The Results
| Metric | Before | After | Change |
|---|---|---|---|
| Containers | 8 | 2 | -75% |
| Page weight from tags | 420 KB | 180 KB | -57% |
| Cross-domain attribution | Broken | Working | Fixed |
| Duplicate events | 1,200/day | 0 | Eliminated |
| ROAS (Meta) | 2.1x | 3.8x | +81% |
| ROAS (Google) | 1.9x | 3.2x | +68% |
| Reported revenue vs. warehouse | 73% match | 96% match | +23% |
The ROAS improvement wasn't magic. It was accuracy. When you stop double-counting, stop missing cross-domain journeys, and stop attributing paid conversions to direct traffic, the numbers reflect reality. The marketing team could finally trust their dashboards and optimize based on true performance.
What We'd Do Differently Next Time
- Start with the data layer audit. We spent 8 hours fixing frontend code that wasn't sending the right events. If we'd audited the data layer first, we'd have caught this earlier.
- Get consent management right before touching tags. The client was using a basic cookie banner that didn't block tags properly. We had to rebuild consent state management as part of the migration. This should be its own project.
- Document the "why" not just the "what." Every tag has a business reason. "Fire Facebook Purchase on checkout complete" is what. "Attribute revenue to Meta campaigns for CMO reporting" is why. The why survives team changes.
Bottom Line
GTM consolidation isn't glamorous. It's cleanup work. But for this client, it recovered $340K in attributed revenue that was being lost to bad data. The marketing team stopped arguing about numbers and started optimizing campaigns. The analytics team stopped maintaining eight containers and managed two.
If you have more than three GTM containers, you almost certainly have this problem. The question isn't whether you're losing attribution — it's how much.