Skip to content

Nine GTM Tags in 90 Minutes (the build I used to bill $2K for)

TRACKING
Nine GTM Tags in 90 Minutes (the build I used to bill $2K for)
Conner Crowe

Quick Take

Tag Manager work has been one of the moats agencies could charge $2-3K/month for. The configuration was finicky enough that founders couldn’t handle it themselves and senior operators were a scarce resource. That moat is shrinking. Last week I closed the tracking gaps on my own site in 90 minutes: three Data Layer Variables, three custom event triggers, three GA4 event tags wired to them, one orphaned trigger deleted, a new container version published. About ninety percent of the work ran with build tooling that has direct API access to GTM. The final publish click had to be done in the UI because of how Google handles OAuth scope verification. The senior operator who built the framework and the tooling that runs it are starting to do the same hour of work. That last detail is the actual story.

The Setup Question

I’ve been writing about The Tracking Stack for about a year. Eight layers, opinionated, documented as a reference architecture for Shopify and lead-gen sites. It’s the framework I use on every client engagement. It’s also what convinced me to start running the configuration with API tooling directly instead of just generating advice about it.

The question I wanted to answer: can this tooling administer Google Tag Manager well enough that I’d run it on a paying client’s container? Not “can it generate the right tag config.” That’s old news. The actual question is operational. Can it read what’s already there, propose the right changes, build them, ship them, and recover when something breaks.

The setup took maybe twenty minutes. The tooling runs a local server that wraps the Tag Manager API. I picked a community-maintained one rather than the hosted alternative because I’d rather not route an admin-level OAuth token through somebody else’s infrastructure. That choice ended up mattering twice during the build.

Once the server was installed and Google OAuth granted, the tooling had access to about a hundred Tag Manager operations: list accounts, read tags, create triggers, update variables, publish versions. From there it was a real ops session, not a demo.

What the Agent Found Before It Built Anything

First pass was the audit. List the accounts I have access to. Find the right one. Pull every tag, trigger, and variable in the container. Compare what was actually firing against what my site’s dataLayer was emitting.

Seven tags, five triggers, three Data Layer Variables. The audit took under a minute. The diagnosis was harder than I expected.

My site fires six custom dataLayer events: cta_click, calendly_open, lead_magnet_submit, contact_form_submit, case_study_view, and blog_post_view. Three of those had GTM tags catching them. The other three were emitting into the void. lead_magnet_submit, in particular, fires every time someone downloads one of my free PDFs. That’s the highest-intent event on the site. It was hitting the floor.

The audit wrote up the gap, ranked the missing pieces, and asked which tier to ship. That conversation took two minutes. I picked the highest-priority tier: close the dataLayer gaps. New DLVs, new triggers, new GA4 tags wired to them.

What It Built

Three Data Layer Variables: DLV - lead_magnet_name, DLV - form_type, DLV - page_path. Each one reads the corresponding key off the dataLayer.

Three custom event triggers: CE - lead_magnet_submit, CE - case_study_view, CE - blog_post_view. Each one fires when its named dataLayer event arrives.

Three GA4 event tags: GA4 - Lead Magnet Submit, GA4 - Case Study View, GA4 - Blog Post View. Each one is wired to its corresponding trigger and forwards the event to the GA4 property already running on the site.

One orphaned trigger deleted. There was a Scroll Depth 50% trigger sitting in the container connected to no tag. The audit flagged it and asked permission to clean it up.

Everything went into the Default Workspace in a single review pass. I checked the diff, approved, and the publish step ran.

That’s where it got interesting.

The Three Things That Broke

The first was a Windows filesystem quirk. The edit-and-restart loop hit a virtualized AppData folder that the writer process could touch but the running server subprocess couldn’t read from. Half an hour of restart cycles before I noticed four stale Node processes, each loading the unpatched server. Killed them all. The fifth spawn loaded the patched file.

The second was the server itself. Its createVariable function had a bug. It hardcoded the parameter shape to { key: 'value' } regardless of the variable type. That works fine for Constants but breaks for Data Layer Variables, which require { key: 'name' } and a dataLayerVersion parameter. Every DLV creation came back with vendorTemplate.parameter.name: The value must not be empty.

Source read, bug identified, thirteen-line patch with three-branch logic for the parameter shape, permission to apply. I authorized. The patch went in, the next variable created cleanly, the build continued. I’ll send the same patch back to the maintainer as a pull request when this post ships.

The third was Google’s own OAuth scope handling. The local server requested two scopes during the consent flow: tagmanager.edit.containers and tagmanager.publish. Both showed up in the auth URL. But when the publish step ran, Google returned Insufficient Permission. The edit scope was honored. The publish scope wasn’t. My read is that publishing a Tag Manager version is one of the sensitive scopes Google flags for unverified OAuth apps, and an unverified app in testing mode can’t actually use it even if the consent flow appeared to grant it.

The build was complete. Nine new entities, all correctly wired. The only thing left was the publish button. I clicked it manually in the GTM UI. That took fifteen seconds.

What This Means For Tracking Ops

If you’re a founder or operator waiting to see what fast in-house tracking ops actually looks like, this is the shape of it. Not the demo version.

The build did the hour of work I’d otherwise be in the keyboard for. Audit, plan, build, surface the diff, approve. Three independent failure modes surfaced and got worked through. Source read, bug patched, the rest of the build continued. A permissions wall I had to step through manually, the rest of the work intact.

What did not happen automatically: crossing an OAuth verification boundary, deleting things I hadn’t explicitly authorized, publishing a production version without my approval. Each refusal was correct. The safety net is doing what it should.

What the next version of this looks like is fairly obvious. The bug gets patched upstream, OAuth verification gets sorted, and the same playbook runs on a real client engagement. The pieces that need a human shrink to “approve the diff” and “click publish.”

That’s a meaningful shift for the tracking layer specifically. Tag Manager work has always been one of the moats agencies could charge for, because the configuration is finicky enough that founders couldn’t handle it themselves. That moat shrinks every quarter now. The senior operator who built the framework and the tooling that runs it are starting to do the same hour of work.

The Receipt

Container GTM-K5F244FD is now on version 18 as of today. The build added three Data Layer Variables, three triggers, three GA4 event tags, and removed one orphan. Version 17 was the pre-build baseline. The diff is the work. The full audit, plan, and build session is documented in this post. The Tracking Stack framework itself is at /frameworks/tracking-stack and unchanged. This post is the case study of operating it.

If you want the same audit run on your own container, that’s the audit call.

One Footnote For Engineers

The MCP server I used is gtm-mcp on npm, maintained by pouyanafisi. The bug I patched is in dist/gtm-client.js, function createVariable. The fix is to branch the parameter array construction on variableType so v returns [{ key: 'name' }, { key: 'dataLayerVersion' }] instead of always returning [{ key: 'value' }]. I’ll send the upstream PR.

Keep going

If this hit, the next two pieces in the same universe:

Free PDF: The 25-page Tracking Stack. No email gate.

Want a review like this on your account?

Want this kind of review
on your account?

Thirty minutes on the phone. Same person on the call as on the work. Walk out with a clear set of next steps.

Book a Call