GTM Engineering
Prove the 10 Hours: A GTM Engineer Automation Ledger
A VP asked what my automations saved and I had a vibe, not a number. Here is the four-rung ledger that tags every flow with hours saved, run count, and failure rate, plus the schema and rollup query that defend the function net of failures.
· 14 min read
“What have the automations saved us?” A VP asked me that in a budget review, and I had a feeling instead of a number. I knew there were around sixty flows running. I knew reps were not manually stamping close dates or copying owners across objects anymore. I knew the routing was faster. But when the question was hours and dollars, I reached for a story, and a story loses to a spreadsheet every time. I got the budget that quarter on trust. Trust is not a plan.
The GTM engineering function has a measurement problem that RevOps does not. A dashboard has open rates. A forecast has accuracy. Automation work is invisible when it succeeds, because a good flow removes the very toil that would have proven it mattered. Nobody logs the twelve minutes a rep did not spend copying a field, so the twelve minutes never show up anywhere, and at budget time the function that quietly saved a thousand hours looks the same as the function that shipped nothing. The fix is to make the savings a first-class record, built in a fixed order so the number that comes out survives an audit.
That order is the whole framework. Four rungs, bottom to top: define the schema that holds the truth, measure the baseline before you destroy it, roll it up net of failures, then defend it in the room. Scroll the ladder, then I will build every rung under it with the schema and the queries.
- L4Defend the function with one query400 hrs/wk
Rank descending for the budget story, rank by failure rate for the maintenance backlog. Two views, one source of truth. When the VP asks what the automations saved, you turn the laptop around instead of telling a story.
- L3Roll up net of failuresnet hrs/wk
Feed runs and failure rate from the platform logs weekly, then compute gross minutes minus cleanup minutes, divided into hours, times loaded cost. A flow that saves 5 hours and fails 8 percent of the time is not a 5-hour flow.
- L2Stopwatch the baseline before you automatemedian of 3
Time the manual task three times, take the median, record it at build time. This is the one field you can never backfill, because automating the task destroys the thing you would measure. Vibed savings do not survive a budget review.
- L1Model the ledger object1 object
One Salesforce custom object, one row per automation, living where the automations live so it inherits platform reporting. The fields that matter are savings, runs, failure rate, cleanup cost, and last-verified date. Skip anything that will not appear in the rollup or the fix list.
Locate your team on that ladder. Most functions never leave the ground, defending a thousand hours of real work with a sentence and a hopeful tone. Each rung above the floor turns one piece of that sentence into a number you can source. By the top rung you walk into the review with a ranked, costed rollup instead of a feeling.
The number you are defending (rung L1 starts with a target)
Set the north star before you build anything, because a ledger without a target is a log nobody reads. Mine is roughly 10 hours saved per week per rep. That is the toil an engineered GTM function should be pulling off each seller’s plate: the field stamping, the copy-paste between systems, the manual routing, the reminder-chasing, the report-building that a flow or a script can own instead. If a 40-rep team is getting 10 hours back each, that is 400 rep-hours a week the function is defending. Put a fully-loaded cost on the hour and the ledger stops being an engineering artifact and becomes a budget document.
The target is not arbitrary. Salesforce’s State of Sales research has reps spending only about 28 percent of their week on selling itself, with the rest lost to manual and administrative work (Salesforce, State of Sales). Ten hours back is a real dent in the other 72 percent, and it is a number a VP recognizes. The trap is measuring gross hours and calling it done. A flow that saves 5 hours a week but fails 8 percent of the time is not saving 5 hours. Every failure spawns a support ticket, a confused rep, and a manual cleanup that eats into the savings. The ledger has to carry the failure rate next to the hours, or it inflates its own numbers the way an automation-debt pile inflates its apparent coverage while quietly rotting underneath.
The ledger schema (rung L1)
Model it as a Salesforce custom object so it lives where the automations live and inherits the platform’s reporting. One record per automation. The fields below are the ones I populate; skip the speculative extras.
Automation_Ledger__c
Name Text "Opp | Stamp Close Date on Won"
Automation_Type__c Picklist Flow / Apex / Script / Data Job
Owner__c Lookup(User)
Status__c Picklist Active / Paused / Retired
Minutes_Saved_Per_Run__c Number(6,1) measured, not guessed
Runs_Per_Week__c Number(8,0) pulled from run logs, refreshed weekly
Failure_Rate_Pct__c Percent failures / total runs, trailing 30d
Cleanup_Minutes_Per_Failure__c Number(6,1)
Loaded_Hourly_Cost__c Currency fully-loaded rep or ops cost
First_Run_Date__c Date
Last_Verified_Date__c Date when a human last confirmed it works
Source_Signal__c Text what triggers it
Two fields carry more weight than the rest. Minutes_Saved_Per_Run__c must be measured, not guessed, and I will show how below. And Last_Verified_Date__c is the honesty check: an automation nobody has confirmed in 90 days is a liability wearing a savings label, exactly the kind of drift that turns a clean stack into a tool-sprawl governance problem.
Measuring minutes-saved without guessing (rung L2)
The soft spot in any ledger is the savings estimate, and it is rung L2 because you cannot skip it and still have a defensible number. Reps will tell you a task took “forever.” That is not a number. Before you automate a task, time the manual version three times with a stopwatch and take the median. Record the baseline in the ledger at build time. This is the one measurement you cannot backfill, so do it while you still have the manual process to observe.
| Task | Manual time (median of 3) | Automated | Minutes saved per run |
|---|---|---|---|
| Stamp close date + notify owner | 3.5 min | 0 (flow) | 3.5 |
| Copy account owner to child opps | 6.0 min | 0 (flow) | 6.0 |
| Build weekly pipeline export | 45 min | 2 min (script + review) | 43.0 |
| Route inbound lead to rep | 4.0 min | 0 (flow) | 4.0 |
| Dedup check on new logo opp | 8.0 min | 0.5 min (flow flag) | 7.5 |
Measured, not vibed. When the VP asks where 43 minutes comes from on the pipeline export, the answer is “I timed the analyst doing it three times before I killed the task,” and that answer ends the conversation. A ledger built on stopwatch baselines survives an audit. A ledger built on rep memory does not.
The rollup query that defends the function (rung L3 and L4)
Once the object is populated and the run logs feed Runs_Per_Week__c, one query produces the number the VP asked for: net hours saved per week and the dollar value, ranked so the biggest wins and the biggest problems both surface.
SELECT
Name,
Automation_Type__c,
Status__c,
Runs_Per_Week__c,
Minutes_Saved_Per_Run__c,
Failure_Rate_Pct__c,
-- gross weekly minutes the automation removes
(Runs_Per_Week__c * Minutes_Saved_Per_Run__c) AS gross_min,
-- minutes clawed back by failures needing cleanup
(Runs_Per_Week__c * (Failure_Rate_Pct__c / 100)
* Cleanup_Minutes_Per_Failure__c) AS cleanup_min,
-- net hours per week
((Runs_Per_Week__c * Minutes_Saved_Per_Run__c)
- (Runs_Per_Week__c * (Failure_Rate_Pct__c / 100)
* Cleanup_Minutes_Per_Failure__c)) / 60 AS net_hours_wk
FROM Automation_Ledger__c
WHERE Status__c = 'Active'
ORDER BY net_hours_wk DESC
SOQL will not do the arithmetic inline, so in practice the derived columns run in the reporting layer or a small script over the query result. The shape is what matters: gross minutes minus cleanup minutes, divided into hours, ranked. The dollar rollup is one more multiply against Loaded_Hourly_Cost__c, and now you have both the hours story for the team and the dollars story for finance from the same source.
What the ledger shows that a flow list does not
A plain inventory of automations tells you what exists. The ledger tells you what earns its keep. The chart below is the same fifty-nine automations viewed two ways: gross hours, which is the number teams instinctively report, and net hours after cleanup, which is the number that holds up. Toggle between them and watch the failing flows collapse.
View as table
| Item | Value |
|---|---|
| Pipeline export | 43 hrs/wk |
| Owner copy | 12 hrs/wk |
| Lead routing | 18 hrs/wk |
| Dedup flag | 9 hrs/wk |
| Close-date stamp | 7 hrs/wk |
The lead-routing flow is the lesson. On gross hours it is the second-biggest win in the stack at 18 hours a week. On net hours it drops to 6, because it fails often enough that reps spend two-thirds of the saved time cleaning up misroutes. Without the failure column, you would defend that flow as a top performer and never fix the thing making it leak. The ledger turns “it runs a lot” into “it runs a lot and bleeds two-thirds of its value,” which is a work item, not a brag.
The failure column is the maintenance backlog
Sort the ledger by failure rate and you have a prioritized fix list that pays for itself in reclaimed hours. This is the same discipline as building idempotent automations: the flows that fail and re-fire are the ones eating the savings, and they surface the moment you track failures as a first-class number instead of a support-ticket afterthought.
| Automation | Runs/wk | Failure rate | Net hrs/wk | Action |
|---|---|---|---|---|
| Lead routing | 300 | 11% | 6.0 | Fix: dedup misroute, reclaim 12 hrs |
| Re-engagement job | 80 | 7% | 3.1 | Investigate stale-signal failures |
| Owner copy | 120 | 2% | 11.8 | Healthy, leave it |
| Dedup flag | 40 | 1% | 8.5 | Healthy, leave it |
| Close-date stamp | 200 | 0.5% | 6.8 | Healthy, leave it |
Fixing the routing flow’s failure rate from 11 percent to 2 percent recovers roughly 12 hours a week that were disappearing into cleanup. That is a higher-return use of engineering time than building a new automation, and the ledger is the only thing that makes it visible. A team without the ledger keeps shipping new flows while the old ones quietly leak; a team with it fixes the leaks first.
Before and after the ledger
| No ledger | With ledger | |
|---|---|---|
| Budget defense | A story about time saved | 400 net hours/wk, $1.2M/yr, sourced |
| Which flow to fix next | Whichever generated the loudest ticket | Highest failure rate x highest run count |
| New vs maintain call | Always build new, it feels productive | Fix the 11% failure flow first, higher ROI |
| Savings estimate | Rep memory, "it took forever" | Stopwatch median at build time |
| Stale automation | Runs forgotten until it breaks | Last-verified date flags it at 90 days |
| What the VP hears | "Trust me, it is working" | "Here is the ranked, costed rollup" |
Where the ledger sits in the flow of work
The ledger works as a live record rather than a spreadsheet you update once a quarter before the review. It gets written at three moments: when you build an automation, every week when the run logs refresh, and whenever something breaks. Wire it into the lifecycle and it stays honest without anyone babysitting it.
Here is how I would build it
Five steps, in order, mapping straight onto the four rungs of the ladder. The first is the one everyone wants to skip and the one you cannot, because you can never recover a manual-time baseline once the manual process is gone.
- 1
Stopwatch the manual task before you kill it
For every automation you build going forward, time the manual version three times and record the median as the minutes-saved baseline. This is the only field you cannot backfill, because automating the task destroys the thing you would measure. Do it at build time, no exceptions.
- 2
Create the ledger object with the failure fields
One custom object, one row per automation. The fields that matter beyond savings are runs-per-week, failure-rate, cleanup-minutes-per-failure, and last-verified-date. Skip speculative fields. If a column will not appear in the rollup or the fix list, do not add it.
- 3
Feed runs and failures from the logs weekly
A scheduled job reads the platform run logs and writes runs-per-week and trailing-30-day failure rate onto each row. This is what keeps the ledger current without a human editing it. Manual entry rots; automated entry survives.
- 4
Run the net-hours rollup and rank it
Gross minutes minus cleanup minutes, divided into hours, multiplied by loaded cost. Rank descending for the budget story and by failure rate for the maintenance backlog. Two views, one query, one source of truth.
- 5
Review it on a cadence and act on both ends
Monthly, look at the top of the net-hours list to defend the function and the top of the failure list to fix it. An automation past 90 days without a verified date gets checked or retired. The ledger is only worth keeping if it changes what you build next.
Why unrecorded work disappears
The reason most GTM engineering functions cannot answer the savings question is not that the work has no value. It is that the value was never recorded, and unrecorded value does not exist at budget time. The ledger is not overhead you add to look rigorous. It is the difference between a function that gets cut in a lean quarter because nobody could prove it mattered, and one that walks in with 400 net hours a week and a dollar figure that survives finance.
Build the object. Stopwatch the next task before you automate it. Feed the run logs weekly and run the rollup before the review. The same push-not-pull instinct that makes an exception digest beat a dashboard applies here: the ledger should surface the one flow that needs fixing, not make you go hunting for it. When the VP asks what the automations saved, do not reach for a story. Turn the laptop around and show the query. The number does the arguing, and the number is on your side because you wrote it down while everyone else was busy building the next flow and forgetting to count.
Keep reading
One email. Every week.
One email a week: a system I built or broke, with the config, the numbers, and what I would change. No roundups, no theory, unsubscribe whenever it stops being useful.
The newsletter opens soon.
Connect a provider in src/config.ts