The 90-Minute Measurement Audit: A Complete Playbook
Six phases, ninety minutes, one page of findings: the repeatable GTM + GA4 audit that catches container rot, consent gaps, and silent conversion breaks.
Every measurement stack drifts. Tags multiply, campaigns end but their pixels stay, a redesign quietly breaks half your click triggers, and eighteen months later nobody can say with confidence which numbers on the dashboard are real. The fix is not a heroic one-off cleanup. It is a repeatable audit you can run in about ninety minutes, on a schedule, with findings that turn directly into fixes.
This playbook walks through the six-phase audit we run on every container that comes through our practice. It assumes a standard setup: Google Tag Manager on the site, GA4 as the analytics backbone, one or more ad platforms taking conversion signals, but the method applies to any stack. Work through the phases in order: each one builds evidence the next one uses.
Why most audits fail before they start
The typical audit dies in one of two ways. Either it becomes an unbounded archaeology project that consumes a week and produces a document nobody acts on, or it is so shallow (a quick scroll through the tag list) that it misses the failures that actually corrupt data. Both failures share a root cause: no fixed scope and no definition of done.
The ninety-minute constraint is the discipline. You are not trying to understand everything; you are trying to answer six specific questions:
- Does the container contain only things that should exist?
- Does the dataLayer carry what the tags expect?
- Does every marketing tag respect consent?
- Do conversion counts match the source of truth?
- Do the platforms agree with each other within tolerance?
- Will you find out before the client does when something breaks next month?
Answer those six and you have a real audit. Everything else is decoration.
Phase 1: Container inventory (15 minutes)
Export the container as JSON before you touch anything. This is both your evidence baseline and your rollback insurance. In GTM: Admin → Export Container, choose the live version, and store the file with the audit date in its name.
Now build the kill list. You are hunting four species of container rot:
- Paused tags older than 90 days. A tag paused in one quarter and still paused in the next is not paused. It is abandoned. Delete it; the export preserves it if anyone ever asks.
- Zombie triggers. Triggers attached to zero tags, or triggers whose CSS selectors no longer match anything on the live site. The second kind is the dangerous one, because the tag it drives silently stopped firing. The silent tag failures method walks through hunting these down.
- Orphan variables. Variables referenced by nothing. Harmless individually, but in bulk they make every future debugging session slower.
- Duplicate tags. Two GA4 configuration tags, two Meta base pixels, or the classic: a hardcoded script in the page template and the same tag in GTM. This is the single most common cause of double-counted conversions.
A quick way to find selector-based triggers that no longer match: open the site, open the console, and test each selector the export file contains:
// Paste in the browser console on the target page
document.querySelectorAll('.add-to-cart-btn').length
// 0 means the trigger behind this selector is dead
If you would rather not test selectors by hand, Tag Verification loads the page and reports which tags actually fired.
Log every finding with a severity and an owner. A finding without an owner is a finding that will still be there next quarter.
Phase 2: DataLayer integrity (15 minutes)
The dataLayer is the contract between the site and the measurement stack, and contracts are only useful when both sides honor them. Pull up your event specification (if one does not exist, this phase produces its first draft, and the e-commerce dataLayer blueprint is a sound starting spec) and walk the critical user journey with Preview mode open: land, browse, add to cart, begin checkout, purchase (use a test order or a staging environment for the last step).
At each step, compare what the dataLayer actually carried against what the spec says it should carry:
{
"event": "purchase",
"ecommerce": {
"transaction_id": "T-10482",
"value": 129.00,
"currency": "INR",
"items": [
{ "item_id": "SKU-2210", "item_name": "Annual Plan",
"price": 129.00, "quantity": 1 }
]
}
}
The failures you are looking for are rarely missing events. They are malformed parameters. The three that cause the most silent damage:
- Numbers as strings.
"value": "129.00"works right up until a platform starts rejecting it. - Missing currency. GA4 drops revenue on monetary events without a currency code, and it does so without an error.
- Inconsistent item identity. SKUs on product pages, parent IDs at checkout, and your funnel analysis quietly becomes fiction.
Record every mismatch as a defect against the page or template that produced it, not against the tag. The tag is downstream of the truth.
Phase 3: Consent coverage (15 minutes)
Consent is now a first-class part of measurement (the Consent Mode v2 playbook covers the setup side), and it is auditable in the same mechanical way as anything else. The question is simple: for every tag that stores or transmits user data, which consent signal gates it, and does the gate actually close?
Run the journey twice: once accepting the banner, once rejecting everything, with the network tab filtered to your vendor endpoints. Then fill in the matrix:
| Tag | Consent signal | Fires on accept | Blocked on reject |
|---|---|---|---|
| GA4 Configuration | analytics_storage | Yes | Cookieless ping only |
| Google Ads Conversion | ad_storage + ad_user_data | Yes | Yes |
| Meta Pixel | ad_storage | Yes | No, firing regardless |
| Hotjar | analytics_storage | Yes | Yes |
That bolded cell is what this phase exists to find. Custom HTML tags are the usual culprits. They predate the consent setup and nobody went back to add the check. Every row with a red cell is a compliance defect with a deadline measured in days, not sprints.
Phase 4: Conversion accuracy (15 minutes)
Pick your highest-value conversion and reconcile it against the business source of truth: the order database, the CRM, the billing system. Analytics will never match the backend to the rupee, but the gap should be stable and explainable. The procedure:
- Pull the last full week of the conversion from the backend system.
- Pull the same week from GA4 and from each ad platform taking the signal.
- Compute each platform’s delta against the backend.
- Compare this week’s deltas to the deltas from your last audit.
Healthy stacks show GA4 at 5–15% under the backend (consent loss, blockers, late conversions) and ad platforms scattered wider owing to attribution models. What you are hunting is movement: a delta that was −8% last quarter and is −24% now means something broke in between: a consent change, a redirect eating parameters, a duplicate filter, a deleted trigger.
The number that matters is not the gap. It is the change in the gap since the last time you measured it.
If the delta moved, bisect the period with GA4’s date comparison until you find the day it shifted, then check the container’s version history for what shipped that day. This is why Phase 1’s export habit matters: version history plus dated exports turns a mystery into a diff.
Phase 5: Cross-platform parity (10 minutes)
Platforms will never agree exactly, but they should agree proportionally, and their disagreements should be constant. Build a one-line parity check for the same week of data:
| Metric | GA4 | Google Ads | Meta | Within tolerance? |
|---|---|---|---|---|
| Purchases | 412 | 388 | 367 | Yes (±15%) |
| Revenue | ₹5.2L | ₹4.9L | ₹4.6L | Yes |
| Signups | 1,240 | 1,180 | 2,905 | No, investigate |
A platform reporting more than GA4 by a wide margin almost always means duplicate firing on that platform: a pixel in both the template and the container, or an event fired on both the click and the destination page. A platform far under usually means a consent gate closed on it or a parameter it requires went missing.
Phase 6: Monitoring, so the next break finds you (10 minutes)
The audit tells you the state of the stack today. Monitoring is what keeps the audit’s findings true tomorrow. The minimum viable setup is one scheduled check and one alert channel:
-- Daily volume guard (BigQuery, scheduled query)
SELECT event_name,
COUNT(*) AS today,
AVG(COUNT(*)) OVER (
ORDER BY PARSE_DATE('%Y%m%d', event_date)
ROWS BETWEEN 7 PRECEDING AND 1 PRECEDING
) AS trailing_avg
FROM `project.analytics_XXXX.events_*`
WHERE event_name IN ('purchase', 'generate_lead', 'sign_up')
GROUP BY event_name, event_date
QUALIFY today < trailing_avg * 0.6;
Any row this query returns is a conversion event running 40% below its own trailing average. Pipe the result into a Slack webhook and you have a smoke detector for your data. If you would rather not maintain the query, GA4 Monitoring runs the same previous-day comparison every morning and posts spikes and drops to Slack. Add a weekly container-version diff (did anything change that nobody announced?) and the monthly synthetic walk of the checkout funnel, and the next silent failure will announce itself within a day instead of a quarter.
The 90-minute schedule, on one line each
| Phase | Time | Output |
|---|---|---|
| 1. Container inventory | 15 min | Kill list with owners |
| 2. DataLayer integrity | 15 min | Defect list against templates |
| 3. Consent coverage | 15 min | Consent matrix, red cells flagged |
| 4. Conversion accuracy | 15 min | Delta table vs. source of truth |
| 5. Cross-platform parity | 10 min | Parity table, outliers flagged |
| 6. Monitoring setup | 10 min | One scheduled check + one alert |
| Write-up | 10 min | Findings ranked by severity |
Turning findings into fixes
The audit ends with a written summary, and the summary has exactly three sections: fix this week (consent gaps, double-counting, broken conversions), fix this quarter (naming cleanup, dataLayer spec gaps, monitoring depth), and accepted risk (things you consciously choose to live with, written down so the choice is visible). Every item carries an owner and a date. An audit that ends in a document is theater; an audit that ends in tickets is engineering.
Two habits make the next audit dramatically faster. First, fix through versions, not hotfixes: every remediation ships as a named container version referencing the audit, so the history reads like a changelog. Second, automate one finding per cycle: each audit, pick the check that cost you the most manual time and script it. After four quarters, most of your ninety minutes is spent reading reports your own tooling generated, which is exactly where AI-assisted auditing shines, catching the drift continuously so the human audit becomes a review rather than a hunt.
Cadence and ownership
Quarterly is the right default cadence for a stable site; monthly if you ship weekly releases or run aggressive paid acquisition, where a broken conversion signal burns budget by the day. The audit also has three event-driven triggers that override the calendar: run it after any site replatform or theme migration, after any consent-management change, and before any season where the numbers really matter: a launch, a funding narrative, or the quarter your board asks about CAC.
Ownership is the part teams skip. The audit needs exactly one named owner, not a committee, who runs the clock, writes the findings, and chases the tickets. In an agency setting this is the analytics lead on the account; in-house it is whoever owns the container’s publish button, because the person who can ship changes is the person who can ship fixes. Rotate the role annually if you like, but never let it be vacant: an unowned audit is a calendar invite that declines itself.
Finally, keep the artifacts. The dated exports, the delta tables, the consent matrices. A year of them tells you more about your measurement program than any single audit can. Trend lines beat snapshots: when the same defect class appears three quarters running, the fix is not another ticket, it is a process change upstream.
The checklist
- Export the live container, dated, before touching anything
- Delete abandoned tags, dead triggers, and orphan variables
- Verify every selector-based trigger still matches the live DOM
- Walk the funnel in Preview mode against the event spec
- Check numbers are numbers and every monetary event carries a currency
- Run the journey with consent rejected; confirm every marketing tag stays silent
- Reconcile the primary conversion against the backend; compare the delta to last quarter’s
- Build the parity table; investigate any platform out of tolerance
- Ship one scheduled volume check wired to Slack
- Write findings as tickets with owners and dates
Ninety minutes, six questions, one page of findings. Run it quarterly and your dashboards stop being a matter of faith. They become something you can defend in any meeting, because you checked, and you can show your work.