The denominator is the whole analysis. The same portal can honestly report an 8 percent win rate and a 30 percent win rate depending on where qualification is drawn, and both numbers are useless unless the definition travels with them. Dave Kellogg's example of a company showing a 66 percent narrow win rate while derailing 94 of every 100 opportunities, for a broad win rate of 4 percent, is the failure mode this skill exists to prevent.
Steps
-
Discover the pipeline, the stage order and the qualification threshold. Call
hubspot.list_pipelinesfor deals to get every stage id, label,displayOrder, the stage probability metadata and the closed and won flags. Then identify which stage the team treats as "qualified", and put that to the user explicitly rather than guessing: it determines the worked win rate and it is the number people argue about. Callhubspot.list_propertiesfor deals and companies to find the real segment property, which may be a customsegment,tieror deal-size band on the deal or may exist only on the company asnumberofemployees,annualrevenueorindustry. Confirmdays_to_close,hs_analytics_source,dealtypeand any custom ACV or MRR property. -
Pull resolved deals by close date for the period metrics.
hubspot.search_dealswithclosedateBETWEEN the window start and end in epoch milliseconds andhs_is_closedEQtrue, requestingdealname,amount,amount_in_home_currency,deal_currency_code,dealstage,pipeline,createdate,closedate,days_to_close,hs_is_closed_won,hs_analytics_source,hubspot_owner_id,dealtypeandclosed_lost_reason. Split won from lost onhs_is_closed_won, never on a stage label, because custom pipelines routinely carry several lost stages. Shard the window by month until each query returns under the 200-row page limit, since this connector returns only the first page and never gives you the next cursor; report the row count per shard and caveat any shard that hits the limit exactly. -
Pull the creation cohort separately, because it answers a different question. Run
hubspot.search_dealsagain with the same window applied tocreatedateinstead ofclosedateand no closed filter, so you can compute close rate (created-to-won) and see how much of the cohort is still open. Without this pull the close rate is unknowable. Cohort by creation date whenever the question is "is it getting worse": measuring by close date in a lengthening-cycle business creates a survivorship illusion, because the deals closing today were created under different conditions. -
Compute narrow and broad win rate, and state the denominator in the output text. Narrow win rate is wins divided by wins plus losses. Broad win rate is wins divided by wins plus losses plus derails, where derails are opportunities that left the funnel without a competitive resolution: in HubSpot these usually sit in a separate disqualified or unqualified terminal stage, or appear as early-stage losses with no
closed_lost_reason. Resolve which stage ids carry that meaning fromhubspot.list_pipelinesand say which ones you treated as derails. Report both numbers side by side, plus the close rate from step 3. Note explicitly that both win-rate variants ignore deals that slipped out of the period rather than resolving: Kellogg's rule of thumb is win a third, lose a third, slip a third. -
Compute cycle length as a distribution and velocity per segment. Use the median
days_to_closeand report the 75th percentile alongside it, because cycle-length distributions have a long right tail and the mean overstates typical duration badly enough to break the coverage math downstream. Then compute velocity per segment as the number of qualified opportunities multiplied by average deal value multiplied by win rate, divided by median cycle length in days, and attribute the formula to Altify, which originated it and trademarked the Sales Velocity Equation. Also compute Ebsta and Pavilion's per-deal form, win rate multiplied by average contract value divided by sales cycle, when comparing sources, since that is the form they publish and apply to channels. Segment on dimensions the portal actually has and report sample size in every row. -
Replace proposal value with realised value where the two disagree.
stripe.list_subscriptionsorpolar.list_subscriptionsover the same accounts gives signed value. CRMamountis frequently the proposal value and is often never corrected after signature, which inflates average deal size and therefore velocity. Report the gap rather than silently picking one side. Usegoogle_sheets.append_rowsto build a velocity tracker, because the four-factor decomposition only means anything across periods. -
Report. One table with a row per segment and columns: resolved deals, won, lost, derailed, narrow win rate, broad win rate, close rate (created-to-won), average deal value, median cycle days, 75th percentile cycle days, and velocity in revenue per day. A second small table showing the four-factor decomposition against the prior period: opportunity count, deal size, win rate, cycle length. Then one sentence naming which single factor moved most, because the remedy differs completely for each. Altify's warning belongs in that sentence when relevant: improving the three numerator levers by 10 percent while the cycle stretches from three to four months erases the entire gain.
Gotchas
- Never publish a win rate without its denominator in the same sentence. HubSpot's own guidance on including no-decision deals in the denominator is that it is a choice rather than a standard, and that "the key takeaway here is to be consistent". Never compare a number you computed to an external benchmark computed on a different denominator, and treat the widely repeated "average B2B win rate is 21 percent" figure as unusable: it has no traceable primary source.
- Close-date cohorting hides a deteriorating funnel. When the cycle is lengthening, win rate by close date looks flat while created-to-won is collapsing, because the two measure different vintages. Report both cuts and label which is which.
- Closed-deal fields do not exist on open deals, and a young cohort is not a result.
days_to_closeis populated only on closed deals and is computed by HubSpot, so cycle length for open pipeline must be derived fromcreatedate, and the two must not share a column without saying so. Relatedly, excluding still-open deals from the win-rate denominator is correct but means the figure is only final once the cohort has fully resolved: for any cohort younger than the 75th percentile cycle length, label the win rate provisional and give the share of the cohort still open. - Small segments produce fake precision. A segment with 6 resolved deals and 3 wins is not a 50 percent win rate. Suppress or explicitly flag any row below roughly 20 resolved deals, and never let such a row drive the recommendation.
- Currency, twice.
amountis in the deal's own currency, so useamount_in_home_currencywhenever more than onedeal_currency_codeappears, and band deal sizes in a single currency or the bands are meaningless. HubSpotamountis a decimal in major units; Stripe amounts are in the smallest currency unit and need dividing by 100 before any comparison. - The 10,000 result cap and the single-sort limit bite together. A multi-year win-rate study exceeds 10,000 deals and paging past the cap returns a 400. Shard by
createdatequarter, and remember the search API permits exactly onesortsrule, so you cannot sort by segment and date at once: sort by date and group in the agent. Archived records never appear in search, so they cannot account for a gap. - A falling win rate is not automatically bad. As a company moves upmarket it enters larger deals it is less prepared for, which depresses win rate while raising revenue. Report win rate next to average deal value and segment mix so a mix shift is not misread as a performance drop.