GTM Operations
Win-Loss on Autopilot: Instrument the CRM So the Data Collects Itself
The quarterly win-loss survey nobody fills out is a design failure, not a discipline failure. Six close-reason values, one validation rule at the moment of close, and 100% of lost deals log themselves.
· 12 min read
Every win-loss program I inherited worked the same way. A quarterly survey went out, three reps filled it out, and someone built a board slide from a sample of three. Then the same people who ignored the survey complained we had no idea why we lost, in a market where B2B win rates fell to 19% in 2025 from about 29% the year before (Ebsta and Pavilion, 2025 B2B benchmarks). When you win one deal in five, the four you lose are your richest dataset, and we were throwing them away because we asked about them three months late, in a separate tool, about a deal the rep had already forgotten.
The problem was never that reps will not tell you why deals die. It is that a free-text “why did we lose” field looks like data and reports like noise. Take 200 lost deals and you get 37 distinct strings: “budget,” “no budget,” “timing,” “bad timing,” “went dark,” “ghosted.” Every one is a different value, so you can never count them, and the analyst gives up and sends a survey. Watch what happens when you replace those 37 strings with six required values and move the question onto the moment of close.
Win-loss data that collects itself is a schema decision, not a research project. You need a close-reason field structured enough to report on, required logic that fires at the exact moment a rep marks a deal Closed Lost, and a reporting recipe that turns the accumulated rows into the slide you used to beg for. All three live in the CRM. None of them is a survey. And the build has a fixed order, because each piece references the one before it, which is why I run it as a five-step framework rather than a pile of fields.
The close-reason picklist is where it lives or dies
A free-text field is worse than nothing, because it costs a rep the same effort to type “ghosted” as to pick from a list, and the typed version is unreportable. The fix is a structured picklist, short enough that a rep will actually pick, specific enough that the buckets mean something. Keep it to a single tier of mutually exclusive reasons. Mine settled here after cutting a bloated 20-value list nobody used:
Closed_Lost_Reason__c (picklist, required at Closed Lost):
- Lost to competitor
- Price / budget
- No decision / timing
- Lost champion / no access
- Product gap
- Bad fit (we should not have chased it)
Competitor__c (picklist, required only when reason = Lost to competitor)
Loss_Notes__c (long text, required, min length enforced)
Two things make this reportable instead of decorative. “Lost to competitor” forces a second required field naming the competitor, so competitive losses roll up by name without anyone typing free text. And “Bad fit” is a deliberate bucket, because a chunk of your losses were never winnable, and separating those from real competitive losses stops you from tuning the sales process to win deals the sales process was never going to win. With buying groups now running 6 to 11 people in the mid-market and 17-plus in enterprise (Gartner and Forrester, 2025), a loss is rarely one clean cause, so six buckets is the ceiling. Add a seventh and rep agreement on which bucket a deal belongs in starts to fall apart, and inter-rater disagreement is how a picklist quietly rots back into noise.
Required at the moment of close, not before
The whole trick is timing. You do not make the field required all the time, because that annoys reps on every open deal for no reason. You make it required at exactly one transition: when Stage moves to Closed Lost. At that moment the rep is already in the record, already clicking, and the deal is fresh. That is the only moment they will give you an honest answer, so that is the only moment you ask.
The diagram is the capture model. The old survey sits three months downstream of the deal, disconnected. The new gate sits on the exact transition, inline, and cannot be skipped.
A before-save validation rule enforces it. In Salesforce terms:
// Validation rule: block Closed Lost without a reason
AND(
ISPICKVAL(StageName, "Closed Lost"),
OR(
ISPICKVAL(Closed_Lost_Reason__c, ""),
AND(
ISPICKVAL(Closed_Lost_Reason__c, "Lost to competitor"),
ISPICKVAL(Competitor__c, "")
),
LEN(Loss_Notes__c) < 20
)
)
That rule does three jobs at once. It blocks the save if the reason is blank. It escalates the requirement when the reason is “Lost to competitor” by also demanding the competitor name. And it enforces a minimum note length so Loss_Notes__c cannot be a single character someone typed to get past the gate. Twenty characters is not a lot, but it is enough to stop “n/a” and force a real sentence.
The rep cannot advance the deal to Closed Lost without answering. So they answer, once, at the moment they know the answer, and the quarterly survey retires itself.
The reporting recipe
Now the data accumulates on its own, one row per lost deal, structured and countable. The report writes itself because you designed the fields to be grouped. Build a summary report grouped by Closed_Lost_Reason__c, filtered to Closed Lost in the period, summing lost ARR and counting deals.
SELECT
closed_lost_reason,
COUNT(*) AS deals_lost,
ROUND(SUM(amount)) AS arr_lost,
ROUND(SUM(amount) / SUM(SUM(amount)) OVER () * 100, 1) AS pct_of_lost_arr
FROM opportunities
WHERE stage = 'Closed Lost'
AND close_date BETWEEN :qtr_start AND :qtr_end
GROUP BY closed_lost_reason
ORDER BY arr_lost DESC;
For competitive losses, a second report grouped by Competitor__c tells you who is actually beating you and for how much, ranked by dollars. That is the report that changes a battlecard, and it exists now without a single interview. A shape I have seen more than once, the 200 deals from the top of this piece:
| Reason | Deals | ARR lost | % of lost ARR |
|---|---|---|---|
| No decision / timing | 41 | $1.9M | 34% |
| Lost to competitor | 28 | $1.6M | 29% |
| Price / budget | 22 | $980K | 18% |
| Bad fit | 19 | $610K | 11% |
| Product gap | 14 | $450K | 8% |
Charted by dollars, the ranking is impossible to argue with, and it points somewhere other than where the room assumes.
View as table
| Item | Value |
|---|---|
| No decision / timing | 1.9M |
| Lost to competitor | 1.6M |
| Price / budget | 1.0M |
| Bad fit | 0.6M |
| Product gap | 0.5M |
Read the top row before you build a discount program to fight the “Price / budget” line. The biggest bucket is “No decision,” a deals-that-never-had-urgency problem whose fix lives in qualification, not deal desk. You only see that because every lost deal is in the table, weighted by dollars, instead of a survey skewed toward the three reps who like filling out surveys.
The weighting is the part the survey could never do
The DissolveField at the top ends on $1.9M for a reason. A survey counts responses, so five reps who each lost a $20K deal to price drown out one rep who lost a $600K deal to a competitor, and the summary slide says price is your problem. Weight by ARR and the picture inverts. Reconcile the table back to the centerpiece: the six structured reasons hold $5.54M of total lost ARR, and the top bucket alone carries $1.9M, or 34% of it. That single number is the budget decision the 37 free-text strings buried.
| What the survey reported | What the weighted data shows |
|---|---|
| Price came up most in the 3 responses | Price / budget is 18% of lost ARR, fourth place |
| Nobody mentioned “No decision” | No decision / timing is 34%, the largest bucket |
| One competitor named, anecdotally | 28 deals, $1.6M, ranked by dollars, per competitor |
| Total lost ARR: unknown, never summed | $5.54M across six countable reasons |
Watch the “Bad fit” line too, because it pays for the whole exercise the fastest. If 11% of your lost ARR ($610K here) is deals you should never have chased, the fix lives upstream in targeting, not in the sales process, and it is a time sink your reps are eating on deals that were dead on arrival. A rep spending a quarter on a bad-fit deal is a rep not working a winnable one, so every bad-fit loss is really two losses: the deal and the pipeline the rep did not build instead. Separating that bucket is how you stop optimizing the sales motion for deals it was never going to win.
Here is the survey program and the instrumented program side by side. Same question, opposite data.
| Quarterly survey | CRM-instrumented capture | |
|---|---|---|
| When it asks | 3 months after close | The moment Stage hits Closed Lost |
| Who answers | The 3 reps who like surveys | Every rep, because the save is blocked |
| Coverage | A sample of three | 100% of lost deals |
| Data shape | Free text, 37 uncountable strings | Six-value picklist, groups cleanly |
| Competitive losses | Whoever remembers to write it | Named field, rolls up by dollars |
| The slide | Begged for, thin, sampled | Runs itself, complete, ARR-weighted |
Here is how I would build it: the self-collecting win-loss program
Three fields and a rule, in this order. The sequence matters because the validation rule references the fields, and the reports reference the picklist values. This is the framework, and it is short on purpose.
- 1
Cut the close-reason list to six values
Six mutually exclusive reasons, one tier, no free text. Include a deliberate Bad fit bucket so unwinnable deals separate from real competitive losses. If you have a bloated 20-value list, cut it now, because rep agreement collapses past six.
- 2
Add the competitor and notes fields
A Competitor__c picklist required only when the reason is Lost to competitor, and a Loss_Notes__c long-text with a minimum length. Two fields that turn a reason code into a battlecard input.
- 3
Write the one validation rule
A before-save rule that fires on the Closed Lost transition and blocks the save on any of three gaps: blank reason, competitor loss with no competitor, or notes under 20 characters. One rule, every escape hatch closed.
- 4
Ship it and wait one quarter
Do not backfill or survey. Let the gate collect a full quarter of structured rows. The data accumulates on its own, one row per lost deal, because the rep cannot close without answering.
- 5
Run the two reports, read the biggest dollar bucket
One grouped by reason summing lost ARR, one grouped by competitor. Read the top ARR bucket before you build any program. The fix usually lives upstream of where the room assumes.
Cut your close-reason list to six mutually exclusive values, add the competitor field and a notes field, and write the one validation rule that fires at Closed Lost. Ship it, wait a quarter, and run the two reports. You will have better win-loss data than any survey produced, complete instead of sampled, ARR-weighted instead of response-counted, and it cost you three fields and a rule instead of a research vendor and a nag nobody answered. For the same self-collecting principle applied to the post-sale side, see you have 40 pipeline reports and one renewal-date field.
Keep reading
One email. Every week.
One email a week: an operating problem I solved or botched, with the model, 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