← All articles

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.

From free-text noise to one budget decision37 uncountable strings dissolve into the reason that matters
37 stringsFree text200 lost deals, 37 distinct strings, blanks, and "n/a". Looks like data, counts like noise.
3 fields
Reason, competitor, and notes, which replace the entire quarterly survey
6
Mutually exclusive close-loss reasons, cut down from a bloated 20-value list
100%
Of lost deals captured at close, not a sample of three who like surveys

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.

Where the capture happens Move the question onto the transition, not into a survey
Deal openClosed Lostgate fires hereReason loggedstructured, countableOld: survey3 months latedisconnected from the moment
The survey asks about a forgotten deal three months late in a separate tool. The validation rule asks at the one moment the answer is fresh and the rep is already in the record.

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:

ReasonDealsARR lost% of lost ARR
No decision / timing41$1.9M34%
Lost to competitor28$1.6M29%
Price / budget22$980K18%
Bad fit19$610K11%
Product gap14$450K8%

Charted by dollars, the ranking is impossible to argue with, and it points somewhere other than where the room assumes.

Lost ARR by reason, one quarter
Every lost deal, weighted by dollars. The biggest bucket is No decision, a qualification problem, not the Price / budget line a discount program would chase.
View as table
ItemValue
No decision / timing1.9M
Lost to competitor1.6M
Price / budget1.0M
Bad fit0.6M
Product gap0.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 reportedWhat the weighted data shows
Price came up most in the 3 responsesPrice / budget is 18% of lost ARR, fourth place
Nobody mentioned “No decision”No decision / timing is 34%, the largest bucket
One competitor named, anecdotally28 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
The survey is a discipline ask that fails. The gate is a schema decision that cannot be skipped.

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.

The self-collecting win-loss program
  1. 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. 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. 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. 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. 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.

win-loss crm reporting

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