The short version: Open a quote, an opportunity or a sales order in NetSuite and ask how likely it is to close. One SuiteQL call reads the record, four weighted factors turn it into a 0–100 score with a tier, and the answer shows you every factor's contribution in points. Nothing is written back. And the answer tells you, every time, that the constants behind it were borrowed from another account's history — because that caveat is the part a finance team actually needs.
This is a walkthrough of a small capability that finance teams keep asking for and sales tools keep answering badly: a defensible number on a single open deal, computed from what NetSuite already knows about it.
Two halves, both honest. First the mechanism — the query, the four factors, the arithmetic, and how to run your first one. Then the limits, including the ones that make this a ranking signal rather than a probability. No number gets oversold. Everything ties back to NetSuite.
The number your forecast is currently rolling up
NetSuite has a probability field on quotes and opportunities. It is a good field. It holds whatever the salesperson typed into it, on the day they typed it.
That is the whole problem. Most reps never fill it in. The ones who do fill it in once, at creation, and nothing recomputes it as the deal ages — a quote can sit at "75%" for eleven weeks while every observable signal around it decays. When FP&A builds a weighted pipeline off that column, the weighting is a snapshot of sales optimism at an arbitrary past moment, rolled forward into a cash forecast that people plan headcount against.
The fix is not to overrule the rep. It is to have a second number, computed the same way every time from attributes nobody types — how long the deal has been open, what it is worth, whether this customer has bought before, which subsidiary it sits under — and then to look at the gap between the two. Where they agree, the forecast line is safe to roll up. Where they diverge, you have found the deal worth a conversation. That gap is the deliverable.
What "score this deal" actually means
It means one record, scored live, off the customer's own data. Not a pipeline report.
The distinction matters more than it sounds. Ranking every open deal, plotting stage-transition velocity, cohorting by rep — that is a much larger job with its own definitional work, and it lives in a governed workbook rather than a chat turn. We wrote up what that looks like in practice in a pipeline health dashboards case study; the hard part there was never the maths, it was agreeing with the client which records even were the pipeline. This is the opposite shape: you already have one transaction open in front of you, and you want a defensible number on it in the next ten seconds.
Three record types qualify — estimate, opportunity and salesorder — and only while they are still undecided. A quote that is Closed or Expired, or a sales order already Billed or Cancelled, has nothing left to predict; the right answer there is to report the outcome and stop.
One more thing is true of a sales order specifically, and it is easy to get wrong: a sales order has already been won. Scoring one measures fulfilment risk, not win probability, and it runs on its own curve. The number means "how likely is this to be delivered and billed as booked", not "how likely is this to close".
Underneath, it is a single SuiteQL call. The record's own fields, plus a correlated subquery counting that customer's decided quotes over the last 24 months:
SELECT
t.tranid AS deal_num,
t.foreigntotal AS deal_value,
t.probability AS rep_probability,
BUILTIN.DF(t.entity) AS customer,
BUILTIN.DF(t.subsidiary) AS subsidiary,
BUILTIN.DF(t.status) AS status_label,
FLOOR(SYSDATE - t.trandate) AS days_open,
(SELECT COUNT(*)
FROM transaction p
WHERE p.entity = t.entity
AND p.type = 'Estimate'
AND p.status <> 'A'
AND p.trandate >= ADD_MONTHS(SYSDATE, -24)) AS prior_decided
FROM transaction t
WHERE t.id = <id>
Two details that cost time if you skip them. FLOOR(...) matters — SYSDATE - t.trandate is fractional days, and an unfloored 12.7 lands in the wrong age band. And prior_decided deliberately does not split won from lost: which status code means "won" differs by tenant, and the factor only needs to know whether this customer has transacted with you before. If you want the mechanics of getting an agent onto SuiteQL in the first place, that is its own write-up.
Where the answer shows up
On the record. That is most of the point — a score you have to leave the transaction to get is a score nobody asks for.
The NSGPT SuiteApp adds subtabs to a record's existing tab strip and an action dropdown to the record itself; Predict Close Probability is one of its verbs. The record header, the action row and the body fields stay exactly as NetSuite renders them. Only the panel under the tab is ours. Below is that surface on a sales order: the record settles on its own tab strip, then the two NSGPT subtabs open in turn — the account context that sits around the deal, and the deal itself in it.
That account, that customer, every balance in it and every percentage on it are invented — a designed illustration of where the answer lives, not a capture of a scored run. The percentages in that panel were drawn for the picture; they are not outputs of the model. The scoring is the four factors below, and they are the thing to hold us to.
The four factors, and what each is really measuring
The composite is four independently weighted factors, each producing a 0–1 probability:
composite = f_age·0.40 + f_size·0.25 + f_history·0.20 + f_subsidiary·0.15
score = composite × 100
Pipeline age carries 0.40 — it is the model, more or less. Past the historical 90th percentile, quotes essentially do not close. It is also the only factor anyone can act on; the other three are properties of the deal you were handed.
Quote / opportunity, days_open |
f_age |
|---|---|
| under 13 (P50) | 0.80 |
| 13–35 (P75) | 0.50 |
| 36–49 (P90) | 0.20 |
| 50 or more | 0.05 |
A sales order runs the fulfilment curve instead — days open there measures delivery drift, not a stalling negotiation:
Sales order, days_open |
f_age |
|---|---|
| under 30 | 0.95 |
| 30–59 | 0.85 |
| 60–89 | 0.70 |
| 90 or more | 0.50 |
Deal size carries 0.25. Historical win rate by value quartile; big deals close at roughly half the rate of small ones.
deal_value |
f_size |
|---|---|
| up to $5,673 (Q25) | 0.744 |
| up to $17,090 (Q50) | 0.674 |
| up to $62,356 (Q75) | 0.616 |
| above $62,356 | 0.477 |
Customer history carries 0.20, straight off prior_decided: 0 prior decided quotes → 0.678, 1–5 → 0.645, 6 or more → 0.603. The counterintuitive direction is real and worth sitting with — in the fitted data, brand-new customers scored slightly better than heavily-transacted ones, most likely because a long history includes a long tail of quotes that were never going to land.
Subsidiary carries 0.15, and its fallback row is the most important number on this page. The subsidiary split was measured on one account with two named subsidiaries. On any other tenant neither name matches, and the model uses 0.630 — its overall base win rate — and says the subsidiary factor is uncalibrated here. That row is not decoration. Picking one of the measured branches on an account it was never measured on would import a win-rate assumption up to 16 percentage points off the base — about 2.4 points on the composite, every one of them invented.
Score to tier:
| Score | Tier |
|---|---|
| 70 or above | High confidence |
| 50–69 | Moderate |
| 30–49 | At risk |
| below 30 | Low probability |
Here is the part that most vendors would leave out. Because every factor is bounded and the weights are fixed, the composite is bounded too. On an account where the subsidiary factor falls back to the base rate, a quote cannot score above 73.6 or below 35.4; a sales order cannot score above 79.6 or below 53.4. "Low probability" is therefore unreachable in practice, and "High confidence" only opens up in the last few points of the quote range.
Read the score as a relative ranking within its own band, not as a calibrated probability of the deal landing. Two quotes at 44 and 61 are telling you something real about which is in more trouble. Neither is telling you that 61 of a hundred such deals will close.
Score your first deal
Five minutes, on a record you already have open.
- Pick an undecided record. An estimate, opportunity or sales order that is still open. If it is Closed, Expired, Billed or Cancelled, the honest answer is the outcome, not a forecast — and you should get exactly that back.
- Ask from inside the record. Use the Predict Close Probability action, or just ask in the panel: "how likely is this to close?", "what's the risk on this one?" Do not type the record number. The embedded panel already sends the record's identity as context, and being asked for a document number you are literally looking at is the fastest way to lose trust in a tool.
- Read the driver table, not the number. Every answer has to carry each factor's observed value, its factor score, its weight and its contribution in points. If a score arrives without that table, it is not usable and you should push back on it.
- Find the dominant driver. It is nearly always age. The sentence you want is which factor cost this deal its points, not what the total was.
- Compare it to the rep's own figure. On quotes and opportunities,
probabilityis what the salesperson typed, stored as a fraction —0.15is 15%. It is not an input to the score. It is the thing worth comparing the score against.
An illustrative run on an anonymised quote — 22 days open, $48,000, a customer with 3 decided quotes in the last two years, on a subsidiary the model was not fitted on:
| Factor | Observed | Score | Weight | Contribution |
|---|---|---|---|---|
| Pipeline age | 22 days open | 0.50 | 0.40 | 20.00 |
| Deal size | $48,000 (third quartile) | 0.616 | 0.25 | 15.40 |
| Customer history | 3 prior decided quotes | 0.645 | 0.20 | 12.90 |
| Subsidiary | uncalibrated → base rate | 0.630 | 0.15 | 9.45 |
| Composite | 57.75 → 57.8 |
57.8/100, Moderate. Weighted value $27,744 — deal value × score ÷ 100, which is the number a forecast actually rolls up. Age is carrying the loss: at 22 days the quote has already fallen out of the band where comparable quotes converted at 80%, and it drops again at day 36. The lever is speed, and it is the only lever on the table.
Four multiplications and a sum. Do the arithmetic in the answer and show it — this is not work that deserves a chart, a saved search or a cell in a workbook. (Where that line falls generally is its own argument.) A read-only run against a sandbox sales order in August scored 79.0 out of 100 — near the top of the band a sales order can reach, which is exactly what a young, in-SLA booked order should look like.
The rep's number is the point
Comparing the score to what the salesperson typed is the most useful thing this capability does, and the easiest thing to overstate.
Where they agree, say so. Agreement is a real signal, and it is the one that lets FP&A roll the line up without a caveat.
Where they are wide apart, say what each one knows. The model sees four historical attributes and nothing else. The rep has been in the room. A rep at 15% against a model at 65 usually means the rep knows something structural — a competitor, a budget freeze, a champion who left — that no NetSuite field records. The model is not detecting optimism there; it is detecting the absence of a field.
Never call the rep wrong. The productive frame is what would have to be true: "on age, size and customer history this one sits near the top of the range the model can produce. At 15% you are saying it is unlike the deals that look identical to it on paper. What's the blocker?" That question is the output. The score is just what makes it askable.
And if probability is null or zero, read nothing into it. Most reps never fill it in. Report the score alone.
Borrowed constants, said out loud
Every weight, threshold and win rate above was fitted on one customer's history: 344 decided quotes and more than 211 won deals over a 24-month lookback, measured in March 2026, on a different NetSuite account — a two-subsidiary US/Canada distributor.
You are almost certainly not on that account. So the model says "borrowed", never "ours", and never "your account's". What that means in practice:
- The mechanism transfers; the constants may not. Age dominating, large deals converting worse, the shape of the decay — those are ordinary and hold up broadly. The exact quartile boundaries and win rates are that account's.
- The subsidiary factor is uncalibrated on your tenant. It uses the 0.630 base row and says so, every time.
- The quartile boundaries are dollar-denominated. If your average deal size sits far outside $5.7K–$62K, the size factor degenerates: every deal lands in the same bucket and contributes an identical number of points — somewhere between 11.9 and 18.6, depending which bucket — across your whole pipeline. A quarter of the weight becomes a constant. The answer should flag that rather than quietly keep scoring.
- Recalibrating is a real job, not a tweak. It means re-running the win-rate-by-quartile and conversion-timing analysis against your own decided history. Worth doing; not something to fake by nudging constants.
Stating provenance in the answer is a deliberate design decision, not modesty. A score that arrives without it gets treated as the system's belief about the future, and a finance team has no way to discount it. A score that arrives with it can be used at the weight it deserves — which, for a first pass, is usually "rank these, then go look at the bottom three." The general version of that argument — that trust in an agent is a record you accumulate, not a decision you make — is here.
The honest limits
- It never writes to NetSuite. The score is recomputed live on every ask and is never persisted to a field, a custom record or the transaction. Nothing downstream can start consuming it as though a person entered it, and it cannot go stale while the deal moves underneath it.
- It is one record, not a pipeline. Ranking all open deals, trend charts, cohort analysis and stage-transition velocity are a different and much larger build.
- It sees four attributes. No competitor, no budget cycle, no champion, no email thread. When the score disagrees with what you clearly believe, that is usually the model missing a field rather than the model knowing better.
- Bounded output is not a probability. The 35.4–73.6 range on quotes is a ranking scale. Treat a 68 as "near the top of what this model can say", not as 68%.
- Garbage upstream still applies. If subsidiaries are missing from the record, or trandate is being set by an import rather than by the deal, the age factor is measuring your integration. That is data foundation work, and it comes first.
The bottom line
A deal close probability score is not a forecast. It is a historical base rate from a comparable account, applied to four things NetSuite already knows about one open record, delivered where that record lives and never written back to it. Used that way — as a second opinion to set against a typed field, and as a ranking that tells you which three deals to go look at — it is genuinely useful, and cheap enough to run on every deal you care about.
Used as a prediction, it is a bounded number wearing a percent sign. The difference between those two readings is the calibration caveat, which is why it ships with every answer instead of living in a footnote.
If you want to see it run against your own NetSuite data — the query visible, the driver table in the answer, and nothing written back — request a walkthrough. Bring the deal your forecast is least sure about; that is where the gap between the score and the rep is easiest to read.
