Coverage is arithmetic, but the arithmetic is only as good as the two numbers nobody checks: which open deals can realistically land inside the period, and what coverage this specific team actually needs. The widely quoted 3x target is the reciprocal of a one-in-three win rate, so quoting it to a team that wins 15 percent of its deals understates the requirement by more than half.
Steps
-
Discover the pipelines and stages before filtering anything. Call
hubspot.list_pipelinesfor deals and capture every pipeline id, every stage id, each stage's label,displayOrder, the stage probability metadata and the closed and won flags. Stage ids are opaque and scoped per pipeline:closedwononly exists in default portals, custom pipelines use numeric strings, and "Negotiation" in one pipeline is a different id from "Negotiation" in another. Then callhubspot.list_propertiesfor deals to confirmamount,amount_in_home_currency,deal_currency_code,closedate,createdate,hs_deal_stage_probability,hs_manual_forecast_categoryandhubspot_owner_idexist, and to find any custom ARR, MRR or segment property. Read theoptionsarray of every enumeration you plan to filter on, because enumeration filters are case-sensitive. -
Establish the period, the target and the pipeline in scope. Ask the user for the quota or revenue target and the period boundaries, and if
hubspot.list_pipelinesreturned more than one pipeline, ask which one or run the analysis once per pipeline. Never pool pipelines: a new-business pipeline and a renewals pipeline have different win rates, different cycle lengths and often differentamountsemantics, so pooled coverage is meaningless. -
Pull the trailing history first and compute your own benchmarks.
hubspot.search_dealsover a trailing 12 months (or three times the median cycle length, whichever is longer) withhs_is_closedEQtrue, requestingcreatedate,closedate,amount,amount_in_home_currency,dealstage,hs_is_closed_won,days_to_closeand the segment property,limit: 200, onesortsrule onclosedate. From this compute win rate as won divided by won plus lost, average deal size, and the median and 75th percentile cycle length. State that definition in the output. Never take the win rate from the user or from a published benchmark, because required coverage of 1 divided by win rate is only valid when the win rate is measured on the same population as the pipeline being covered. -
Pull open in-period pipeline, and pull the invisible buckets explicitly.
hubspot.search_dealswithpipelineEQ the pipeline id,dealstageNOT_IN the won and lost stage ids, andclosedateBETWEEN the period start and end in epoch milliseconds, requestingdealname,amount,amount_in_home_currency,deal_currency_code,closedate,createdate,dealstage,hs_deal_stage_probability,hubspot_owner_idandhs_manual_forecast_category. Then run two more queries that the period filter would otherwise hide: open deals withclosedateLT today, and open deals withNOT_HAS_PROPERTYonclosedate. Report both counts separately and exclude them from in-period coverage. -
Narrow the window until the result set fits one page, then say so. The HubSpot search tools in this connector return only the first page and do not hand back the next cursor, so the maximum you can see in one call is 200 rows and the default is 100. Shard every query by
createdatemonth, byclosedatefortnight or byhubspot_owner_iduntil each shard comes back under the page limit, and sum the shards. Report the row count you actually retrieved for each shard, and if any shard hits the limit exactly, label that figure as potentially truncated rather than presenting it as a complete total. Pace calls at or below 5 requests per second: the search API returns no rate-limit headers, so you cannot self-throttle reactively. -
Add what is already banked, then reconcile it against money.
hubspot.search_dealswithdealstageIN the won stage ids andclosedateinside the period gives closed won to date. Leaving this out overstates the gap. Then check it against cash withstripe.list_charges,stripe.list_invoiceswithstatus: "paid",polar.list_orders, ordodo_payments.list_paymentswithstatus: "succeeded"over the same window. Stripe amounts are in the smallest currency unit, so divide by 100; HubSpotamountis a decimal in major units, so do not. Where the two disagree, report the discrepancy: a coverage report built on a closed-won number finance does not recognise is worthless. Usegoogle_sheets.add_sheetandgoogle_sheets.append_rowswith a run date if the user wants coverage tracked week over week, which is the only way to see it deteriorating. -
Report. One row per owner plus a total row, with columns: quota, closed won to date, open in-period pipeline, stage-weighted pipeline, history-weighted pipeline, unweighted coverage, required coverage (1 divided by measured win rate), gap in currency, and deals needed at the current average deal size. Show the excluded buckets (past close date, no close date, zero or missing amount, aged beyond twice the median cycle) as their own lines with amounts. Then one sentence of judgement: whether the gap is closeable with existing pipeline or requires new creation, and how many days of pipeline creation at the current observed rate that represents.
Gotchas
- The connector returns one page only, and the cursor is unrecoverable.
hubspot.search_dealsaccepts anafterparameter but the response never carriespaging.next.afterback to you, and HubSpot's cursor is opaque so it cannot be reconstructed. Any total summed from a single call silently caps at 200 rows and looks completely plausible. Narrow or shard until each result set fits one page, and state the retrieved row count with a truncation caveat. dealstageis an opaque per-pipeline internal id. Never pattern-match a stage label to decide what is open, won or lost. Resolve the ids and the closed and won flags fromhubspot.list_pipelines, per pipeline, every run. Stage ids change when someone edits a pipeline.hs_deal_stage_probabilityis almost always unmaintained. It defaults from the stage configuration and nobody revisits it, so stage-weighted pipeline is a restatement of your stage mix rather than a forecast. Flag any stage where the configured probability differs from the observed stage-to-won rate by more than 15 points.- HubSpot forecast category labels are positional, not semantic. HubSpot's own documentation states the category descriptions "are based on the sequential order of the forecast categories, not the forecast category's name", so in a customised portal a category named Commit sitting second carries the definition "low likelihood of closing". Read the portal's actual ordering from the property options before trusting
hs_manual_forecast_category. HubSpot also has no Omitted category; that is Salesforce terminology. closedateon an open deal is a rep's guess, and often a stale one. Ebsta and Pavilion's 2024 report, built on 4.2 million opportunities, found 31 percent of all open opportunities are already past their close date, and their 2023 report found 89 percent of close dates are set to the last day of a calendar month. Both are detectable here and both mean the in-period numerator is softer than it looks.- Exclude pipeline that cannot land in time. Salesloft's rule is to remove deals aged beyond twice the average cycle from qualified coverage. Ebsta and Pavilion put a number on why: opportunities open longer than twice the average sales cycle have roughly a 3 percent chance of closing. Report them separately as "needs to be created".
- Test, demo and integration-created deals inflate the numerator. Sample deals shipped with the portal, Zapier or form-created deals with
amountunset, and bulk-import duplicates all sit in open pipeline. Count open deals with zero or missingamountas a separate line and exclude them from weighted pipeline rather than treating them as real zero-value deals. Archived records never appear in search at all, so they cannot be used to explain a discrepancy.