CRM deals, pipelines and stage history
Each stage visit becomes a row with entry and exit
Entries, conversion and time per stage
Dashboard, pipeline review and AI agent
What it solves
- Each stage's conversion comes from the deal's real path, not from the stage it sits in today.
- The bottleneck shows on two axes: where most deals are lost and where they wait the longest.
- Every dashboard and the AI agent count stage, win and loss the same way.
Business questions it answers
Ask your agent these questions once the case is live.
What you need
- CRM connected to Nekt with stage change history (or a daily snapshot of stages saved from now on)
- The list of pipelines that are sales funnels, the official stage order and which stages are side stages (no-show, paused, nurturing)
What you get
- A table with one row per deal visit to a stage, with entry, exit and duration
- Monthly funnel by pipeline and stage, with zero where nothing moved
- Dashboard of conversion and time per stage
- An AI agent that answers funnel questions with the right definitions
Where to activate
The month's funnel with conversion and time per stage, and the bottleneck highlighted.
Answers where the funnel loses deals and how long each stage takes, saying whether the reading is cohort or production.
Notice when a stage's conversion falls below its last three months average.
The rules are settled, but every CRM stores history differently. If yours does not, start a daily snapshot of stages today. The detailed view has the business rules, the pitfalls and the acceptance test. Or send everything straight to your agent with the Nekt MCP.
The CRM shows each deal only in the stage it is in right now. Counting the funnel from that snapshot misses every deal that passed through a stage and moved on, or was lost, and counting all pipelines together mixes sales with customer records. The result is dashboards with different conversions for the same stage and a bottleneck nobody can point to.
How it is usually done, and the rule
Each row is a mistake that already happened in practice.
Build one row per deal visit to a stage, with entry and exit dates, from the CRM history: propertiesWithHistory.dealstage in HubSpot (it comes newest record first, so sort by date), deal_flow in Pipedrive, OpportunityHistory in Salesforce, status change events in Kommo, stage_history in Bitrix24, firstTimeIn per phase in Pipefy. If the CRM keeps no history, save a daily snapshot of stages from today or capture changes by webhook. In HubSpot, hs_v2_date_entered_<stageId> keeps only the last entry into the stage: it cannot be used as the first entry or to build visits.
Classify every pipeline before counting: acquisition funnel, customer records, operations queue, portfolio, post-sale, recruiting or events. Only acquisition funnels enter conversion. In a real case, counting a customer pipeline together with sales produced a false win rate of about 91%. Also exclude archived and draft pipelines. A partner pipeline, or any pipeline with more than one won stage, stays out until someone declares which stage is the win.
A deal reached stage N if it entered N or any later stage of the same funnel. A deal that skipped a stage counts in the skipped stage, with the date of the first later stage. A lead created in a stage by automation shows up in history only with its exit: use the exit date as the entry. The order is the official one, declared by the team: side stages (no-show, paused, nurturing) are not progress and stay out of the order. The CRM display order is only a starting point, and stage probability does not always rise with position.
The loss stage does not move a deal forward. The loss counts at the last active stage before it. Status comes from the CRM semantics (won, lost, open), never from the stage name, and a stage named after a sales rep is not a stage. In a real case, treating the move to Lost as a step forward created more than a thousand false progressions. A loss that comes straight from another pipeline, or a deal created already lost, has no active stage in the funnel: count it separately as a loss with no stage, not at the first stage.
In HubSpot, the stages' metadata.isClosed arrives as true for all of them through the connector, including open ones. Derive closed and won from the stage probability (1.0 won, 0.0 lost) or from hs_is_closed_won and hs_is_closed on the deal. In Bitrix24, use stage_semantic_id; in Kommo, statuses 142 (won) and 143 (lost) apply to any funnel.
History holds repeated records of the same stage with no real change. Mark the start of a visit only when the stage differs from the previous record of the same deal; otherwise the last entry date gets inflated. For the funnel, use the first entry into each stage; for time, add up all visits.
Cohort funnel: start from deals created in the month and follow how far each one got. Monthly production: each stage counted in the month of its own event. Both readings are valid, but never filter the cohort by the date of a later stage, and never add creation month to close month.
For a deal still open, time in the current stage is measured up to today and grows every day. Show time in completed stages separately from the age of deals still in the stage, or the average rises on its own. Time is only measured in open stages: time sitting in Won or Lost is not funnel time. Convert dates to America/Sao_Paulo only once: Bitrix24 stores in the server time zone, other CRMs in UTC.
A deal moved to another pipeline leaves the original funnel and changes the past if the count uses the current pipeline. Assign each visit to the pipeline of the stage where it happened. In many CRMs, won deals are moved to an active customers pipeline right after: a win counts by the visit to the won stage, not by the current stage.
History keeps ids of stages that were deleted or recreated in the CRM and no longer appear in the pipeline's stage list. Without a mapping, those visits drop out of the funnel or end up with no pipeline. Map each old stage to its current equivalent, or declare that it stays out, and check that no visit is left without a stage.
Where history and status come from in each CRM
History builds the visits; status tells whether the deal was won, lost or is still open.
Definitions
Pitfalls that already happened
An active customers pipeline counted together with sales pushed the win rate close to 91%.
Classify pipelines before any calculation.
Stages with no deals in the month vanished from the output and the funnel had missing steps.
Build the grid of all stages and fill it with zero.
Using the deal's update date as the entry reset the time with every field edit. Using HubSpot's hs_v2_date_entered_<stageId>, which keeps only the last entry, pushed deals that returned to a stage into the wrong month.
Use the first entry into the stage from the stage history (in HubSpot, propertiesWithHistory.dealstage).
Integration test deals, imported lists and cards lost as duplicates inflated entries.
Exclude by loss reason and by import source, with the list declared. In HubSpot: a duplicate or invalid lead closed_lost_reason, hs_was_imported on the deal and sourceType IMPORT in the stage history.
The MQL definition lived in a BI measure and two dashboards showed different numbers in the same leadership meeting.
Define stage and metric once, in the transformation, and have the dashboards read from it.
A stage history only started recording months after the CRM, and the earlier months held about a third of the real changes.
Declare from when the funnel is reliable and do not compare before that.
Acceptance test
The case is done when every item passes.
- For a closed month, the total deals created in acquisition pipelines, net of the declared exclusions, matches the CRM deals created report (difference under 1%). By pipeline, assign each deal to the pipeline where it was created: the CRM report uses the current pipeline and diverges when deals change pipeline.
- Wins in the month match the deals that passed through the won stage in the month, including those later moved to another pipeline.
- In every cohort, entries into stage N+1 never exceed entries into stage N.
- Every funnel stage shows up in every month, with zero when nothing moved.
- No pipeline classified as records, portfolio, operations or partners appears in the funnel, and no visit is left with an unmapped stage.
Agent package
Paste into Claude, ChatGPT or any client with the Nekt MCP connected.
Sales funnel by stage - Source: stage visits table (one row per deal x visit to a stage, with entry, exit and duration). Do not count the funnel by the deal's current stage. - In HubSpot, visits come from propertiesWithHistory.dealstage. hs_v2_date_entered_<stageId> keeps only the last entry: never use it as the first entry. - Only pipelines classified as acquisition funnels count, with a single won stage. Customer, portfolio, operations, partner, recruiting and events pipelines stay out. - Reached stage N = entered N or any later stage of the same funnel. A deal that skipped a stage counts in the skipped stage. Side stages (no-show, paused) are not progress. - A loss counts at the last active stage before the loss, in the same pipeline. Lost is not progress. A loss coming from another pipeline or from a deal created lost is a loss with no stage. - Won and lost from the CRM semantics (in HubSpot, stage probability 1.0 or 0.0). Never from the stage name or metadata.isClosed. - Won = having passed through the won stage, even if the deal now sits in another pipeline. - Always say whether the reading is cohort (creation month) or production (event month). Do not mix them. - Time in stage: completed visits to open stages only; Won and Lost have no time. Age of deals still in the stage is a different metric. - Dates in America/Sao_Paulo. Client exceptions: - <client>: funnel pipelines = <list>; stage order = <list>; side stages = <list>. - <client>: deleted stages in history = <old id> -> <current stage or out>. - <client>: top of funnel exclusions = <loss reasons and import sources>. - <client>: history reliable from <date>; before that, only the monthly snapshot.
Build the "Pipeline view (funnel)" case in my Nekt workspace following the rules at https://academy.nekt.com/en/casos/visao-do-pipeline.md. 1) Find the source tables and confirm the columns the rules use. 2) Create a transformation at the stated grain applying every rule. 3) Run the acceptance test and show me the result of each item. 4) Create the case's context document in the workspace, leaving my clients' exceptions open for me to fill in.
Variations
The same table with the deal owner at the time of each visit, not the current owner.
Daily stage snapshot saved from now on, or a stage change webhook, until there are enough months.
Link the lead object to the deal through conversion (in Salesforce, convertedOpportunityId) to get lead, MQL, SQL and sale in one row.