# Before an AI agent automates your sales process, mine how it really runs: Salesforce OpportunityHistory and pandas

> Source: <https://dev.to/juanma_garrido_1974/before-an-ai-agent-automates-your-sales-process-mine-how-it-really-runs-salesforce-18lm>
> Published: 2026-11-13 18:40:22+00:00

Every few years a new technology arrives promising to fix how companies work: ERP, then CRM, then the cloud, now AI agents. And in many mid-sized companies the same things keep happening. A manager asks for the numbers in Excel because he does not trust the system. Sales reps keep their own notes. Three reports give three totals. An approval that was supposedly automated ends up in a WhatsApp message.

An AI agent follows the process you give it. The risk is that you give it the documented process, while the company runs a different one. This post shows a cheap way to see the process as it actually runs, using data Salesforce already keeps, before you automate anything.

For opportunities, Salesforce writes a row to `OpportunityHistory` every time the stage, amount or close date changes. No setup needed:

```
SELECT OpportunityId, StageName, Amount, CloseDate, CreatedDate, CreatedById
FROM OpportunityHistory
WHERE CreatedDate = LAST_N_DAYS:180
ORDER BY OpportunityId, CreatedDate
```

Export it to CSV (Data Loader, a report or the CLI). That is an event log: case id, activity, timestamp, resource. It is the minimum input for basic process mining.

``` python
import pandas as pd

log = pd.read_csv("opportunity_history.csv", parse_dates=["CreatedDate"])
log = log.sort_values(["OpportunityId", "CreatedDate"])

# keep only real stage changes (amount or date edits also create rows)
prev = log.groupby("OpportunityId")["StageName"].shift()
log = log[log["StageName"] != prev]

variants = log.groupby("OpportunityId")["StageName"].agg(" > ".join)
print(variants.value_counts().head(10))
```

The documented process is usually one line. The output is usually many. Look at the top ten and ask which one the AI agent would be told to follow.

```
paths = log.groupby("OpportunityId")["StageName"].apply(list)

went_back = paths.apply(lambda s: len(s) != len(set(s)))
jumped = paths.apply(lambda s: len(s) <= 2)

print(f"went back to an earlier stage: {went_back.mean():.0%}")
print(f"two steps or fewer from creation to last stage: {jumped.mean():.0%}")
```

An opportunity that goes from the first stage straight to Closed Won was probably worked somewhere else and updated at the end. That is the CRM nobody fills in, measured.

```
log["next_stage"] = log.groupby("OpportunityId")["StageName"].shift(-1)
log["next_at"] = log.groupby("OpportunityId")["CreatedDate"].shift(-1)
log["wait_days"] = (log["next_at"] - log["CreatedDate"]).dt.total_seconds() / 86400

waits = (log.dropna(subset=["next_stage"])
            .groupby(["StageName", "next_stage"])["wait_days"]
            .agg(["count", "median"])
            .sort_values("median", ascending=False))
print(waits.head(10))
```

Long waits between two stages are often a handoff between areas, or an approval that happens outside the system.

```
movers = log.groupby("CreatedById").size().sort_values(ascending=False)
print(movers.head())
```

If one or two users move most of the stages for a team of reps, someone is collecting updates from a spreadsheet or a chat and typing them in. Ask them how the information reaches them.

The numbers do not tell you why. They tell you who to talk to and which questions are still open in the organization:

| Signal | Open question | 
|---|---|
| Many variants for the same sale | Which process is the real one, and who decides that? | 
| Skipped stages | What does the rep get back for updating the CRM? | 
| Long waits between stages | Which area owns that handoff? | 
| One person moving everyone's records | Where does the information actually live? | 

If you automate before answering these, the agent will do the same things faster, including the wrong ones.

When the ERP is native to the same Salesforce org, as with [EGA Futura ERP](https://www.egafutura.com/erp/erp-para-salesforce), the same approach extends to orders, invoices and payments, because the history lives in one database. That makes the log longer. It does not answer the questions for you.

I wrote the business side of this, in Spanish, as [why we still ask for the numbers in Excel with CRM, ERP and AI](https://discover.egafutura.com/por-que-seguimos-pidiendo-los-numeros-en-excel-cuatro-problemas-que-no-arregla-el-software/?utm_source=devto&utm_medium=referral&utm_campaign=amplify).

*Juan Manuel Garrido, founder of EGA Futura.*
