---
id: casos/visao-do-pipeline
area: RevOps
usage: widely used
effort: medium
grain: Deal x stage visit (entry, exit, duration); rolled up to month x pipeline x stage
testado_com_agente: Oct 2026
---

# Pipeline view (funnel)

How many deals enter each stage, how many move on and how long they stay, rebuilt from the CRM history and not from today's snapshot.

## Executive summary

**For:** Sales and RevOps teams that need to know where the funnel gets stuck, with one number every dashboard repeats.

**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.

**Why it matters:** The CRM stores the current stage, so a deal lost at proposal disappears from the proposal stage and a deal that skipped a stage never shows up in it. Without the history, stage conversion is a guess and the team invests in the top of the funnel when the problem is in the middle. It is the base for forecasting, target coverage, deal health and the funnel by channel.

**Business questions it answers**

- At which stage did we lose the most deals this quarter?
- How long does a deal take, on average, from proposal to won?
- How many deals entered negotiation in September and how many have already moved on?
- Did the meeting to proposal conversion drop compared with last quarter?
- Which stage holds the deals that have been waiting the longest?

**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

## Detailed

### The problem

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.

### Business rules

1. **The funnel comes from history, not from the snapshot.** 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. (Avoid: Count deals by current stage and call it a funnel.)
2. **Only acquisition pipelines enter the funnel.** 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. (Avoid: Add up every pipeline in the CRM.)
3. **Reaching a stage counts even if it was skipped.** 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. (Avoid: Require the deal to have gone through every stage.)
4. **Lost is not progress.** 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. (Avoid: Order stages by position and treat Lost as the last one.)
5. **Closed and won come from semantics, not metadata.** 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. (Avoid: Filter open stages by metadata.isClosed = false.)
6. **A new visit only when the stage changes.** 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. (Avoid: Take the latest date of each stage in the raw history.)
7. **Cohort and production do not mix.** 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. (Avoid: Conversion = deals won in the month / deals created in the month.)
8. **Time in an open stage stays separate.** 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. (Avoid: Average days in stage mixing completed and open visits.)
9. **A deal moved to another pipeline does not vanish.** 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. (Avoid: Count wins by the deals sitting in the Won stage today.)
10. **A deleted stage is still in the history.** 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. (Avoid: Silently drop visits whose stage id is no longer in the pipeline.)

### 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.

- **HubSpot** → History in propertiesWithHistory.dealstage, newest first; hs_v2_date_entered keeps only the last entry (`dealstage history · probability 1.0 won · 0.0 lost`)
- **Pipedrive** → History in deal_flow; deal status (`status = won | lost | open`)
- **Salesforce** → History in OpportunityHistory; closed and won on the opportunity (`IsClosed · IsWon`)
- **Bitrix24** → History in stage_history; stage semantics (`stage_semantic_id = P | S | F`)
- **Kommo** → History in status change events; global won and lost ids (`status 142 won · 143 lost`)
- **RD Station CRM** → Native status; paused counts as open (`won | lost | ongoing | paused`)
- **Pipefy** → Date per phase; if the card went back to Lost, the last time counts (`firstTimeIn · lastTimeIn`)

### Definitions

| Metric | Definition |
|---|---|
| Stage entry | a deal that entered the stage, or a later stage of the same funnel, for the first time; counted once per deal, in the month of entry |
| Stage conversion | deals that reached the next stage / deals that entered the stage, within the same cohort |
| Stage loss | lost deals whose last active stage before the loss was this one, in the same pipeline; a loss coming from another pipeline or from a deal created lost is a loss with no stage |
| Time in stage | exit - entry of each completed visit to an open stage, in days; sum of visits when the deal returns to the stage; Won and Lost have no time |
| Age in current stage | today - entry into the current stage, for open deals only |
| Win rate | won / (won + lost) among the cohort's closed deals; open deals stay out of the denominator |
| Cycle | won date - creation date, for won deals only |
| Bottleneck | the stage with the lowest conversion and the stage with the highest median time, shown side by side |

### Pitfalls

- **False wins from a records pipeline.** An active customers pipeline counted together with sales pushed the win rate close to 91%. How to avoid: Classify pipelines before any calculation.
- **Zero stages disappear.** Stages with no deals in the month vanished from the output and the funnel had missing steps. How to avoid: Build the grid of all stages and fill it with zero.
- **Wrong date used as entry.** 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. How to avoid: Use the first entry into the stage from the stage history (in HubSpot, propertiesWithHistory.dealstage).
- **Test, import and duplicates at the top.** Integration test deals, imported lists and cards lost as duplicates inflated entries. How to avoid: 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.
- **Definition stuck in the BI tool.** The MQL definition lived in a BI measure and two dashboards showed different numbers in the same leadership meeting. How to avoid: Define stage and metric once, in the transformation, and have the dashboards read from it.
- **History that starts late.** A stage history only started recording months after the CRM, and the earlier months held about a third of the real changes. How to avoid: Declare from when the funnel is reliable and do not compare before that.

Validated on real data: In Nekt's own HubSpot account, every stage of every pipeline arrives with metadata.isClosed = true, including open ones. And more than 8 in 10 deals that passed through the won stage of the sales pipeline now sit in another pipeline: counting wins by current stage would show less than a tenth of the real wins.

### Acceptance test

- [ ] 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.

### Context document

```text
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.
```

### Variations

- **By owner:** The same table with the deal owner at the time of each visit, not the current owner.
- **CRM without history:** Daily stage snapshot saved from now on, or a stage change webhook, until there are enough months.
- **From lead to sale:** Link the lead object to the deal through conversion (in Salesforce, convertedOpportunityId) to get lead, MQL, SQL and sale in one row.
