Bad opportunity data rarely looks dramatic. It shows up later as an inflated forecast, a missing renewal term, or a Closed Lost report nobody can trust.
A few well-scoped Salesforce validation rules stop that drift at the save button. For SaaS opportunities, the goal is to request evidence when a rep reaches the stage where that evidence should exist.
Start with the fields and sales-process values already in your org, then add each rule in a sandbox.
Prepare the Opportunity fields before writing formulas
Validation rules do not fill in data or calculate revenue. They evaluate a formula each time Salesforce attempts to save a record. When the formula returns TRUE, Salesforce blocks the save and displays an error.
Separate standard fields from SaaS placeholders
Salesforce provides several Opportunity fields that work well in basic revenue checks. Add custom fields only when your sales process needs information Salesforce does not store by default.
| Field purpose | API name | Field type | Status |
|---|---|---|---|
| Opportunity stage | StageName | Picklist | Standard |
| Estimated deal value | Amount | Currency | Standard |
| Expected close date | CloseDate | Date | Standard |
| Closed status | IsClosed | Checkbox | Standard |
| Won status | IsWon | Checkbox | Standard |
| Subscription term | Subscription_Term_Months__c | Number | Placeholder custom field |
| Expected annual recurring revenue | Expected_ARR__c | Currency | Placeholder custom field |
| Closed Lost reason | Loss_Reason__c | Picklist | Placeholder custom field |
These custom field names are placeholders. A field called “Expected ARR” in your org may have a different API name. Also, do not validate an editable ARR field if it is actually a read-only formula calculated from products or subscriptions.
Find the correct API names in Salesforce
Open Setup > Object Manager > Opportunity > Fields & Relationships. Select a field, then copy its API Name from the field detail page.
Custom fields end in __c, while standard Opportunity fields use names such as Amount, CloseDate, and StageName. Use the Formula Editor’s Insert Field option instead of typing API names from memory.
Check your Opportunity stage labels too. A formula that references "Proposal/Price Quote" will fail to match an org that uses "Proposal" or "Quote Sent".
Create Salesforce validation rules with clear scope
Go to Setup > Object Manager > Opportunity > Validation Rules > New. Give each rule a purpose-based name, such as Require_ARR_At_Proposal, then add a short description that explains when the rule should fire.
The formula must return TRUE when Salesforce should reject the save. Salesforce’s sample Opportunity validation formulas are useful references for this conditional logic.
Limit rules to the right opportunity type
A validation rule applies to every Opportunity unless its formula limits the scope. This matters when new business, expansions, renewals, channel deals, and internal test opportunities use different stages or field requirements.
For example, add this condition to a rule that should apply only to one record type:
$RecordType.DeveloperName = "New_Business"
Use the record type’s Developer Name, not its display label. Confirm it in the target org before activating the rule. Record types and sales processes often differ between a sandbox and production.
Require commercial data at the right sales stage
Start with rules that protect forecast quality. Early-stage opportunities can remain incomplete, while late-stage opportunities should contain commercial details a founder or sales lead can trust.
Require a positive amount before Closed Won
This formula assumes your org uses the stage label "Closed Won". It blocks the save when a rep closes an opportunity as won without a positive Amount or a Close Date.
AND(ISPICKVAL(StageName, "Closed Won"), OR(ISBLANK(Amount), Amount <= 0, ISBLANK(CloseDate)))
Use an error message such as: Enter a positive Amount and a Close Date before marking this opportunity Closed Won.
Set the error location to Amount. Salesforce already treats CloseDate as a required Opportunity field, so the date condition may be redundant in standard save behavior. Remove ISBLANK(CloseDate) if the standard required-field message is enough for your team.
Require subscription term and expected ARR at proposal
For subscription businesses, an Amount alone can hide the economics of a deal. A $24,000 opportunity is not comparable when one term is 12 months and another is 36 months.
This version requires both a term greater than zero and Expected ARR before a rep reaches Proposal, Negotiation, or Closed Won.
AND(OR(ISPICKVAL(StageName, "Proposal/Price Quote"), ISPICKVAL(StageName, "Negotiation/Review"), ISPICKVAL(StageName, "Closed Won")), OR(ISBLANK(Subscription_Term_Months__c), Subscription_Term_Months__c <= 0, ISBLANK(Expected_ARR__c), Expected_ARR__c <= 0))
Use an error message such as: Enter a subscription term and Expected ARR before advancing this opportunity.
Some teams need one of these values, rather than both. In that case, replace the final OR(...) group with this condition, which fails only when both values are blank or zero:
AND(OR(ISBLANK(Subscription_Term_Months__c), Subscription_Term_Months__c <= 0), OR(ISBLANK(Expected_ARR__c), Expected_ARR__c <= 0))
Block unsafe stage changes and capture lost-deal data
Stage movement is where pipeline data often becomes unreliable. A focused validation rule can stop the move without forcing reps to complete every field on day one.
Prevent stage advancement without required forecast data
This rule checks whether a rep changes StageName into a later stage. It then requires both Amount and Expected ARR to be populated with positive values.
AND(ISCHANGED(StageName), OR(ISPICKVAL(StageName, "Proposal/Price Quote"), ISPICKVAL(StageName, "Negotiation/Review"), ISPICKVAL(StageName, "Closed Won")), OR(ISBLANK(Amount), Amount <= 0, ISBLANK(Expected_ARR__c), Expected_ARR__c <= 0))
Place this error at the top of the page because it refers to multiple fields: Add a positive Amount and Expected ARR before moving to this stage.
Because this rule uses ISCHANGED(StageName), it targets stage transitions. Add a separate completeness rule if users can create records directly in a late stage or if older incomplete records need correction.
Require a loss reason for every closed loss
This formula assumes Loss_Reason__c is a picklist. It uses IsClosed and IsWon, so it works even when your org’s Closed Lost stage label differs from Salesforce’s default wording.
AND(IsClosed, NOT(IsWon), ISBLANK(TEXT(Loss_Reason__c)))
Use this error message: Select a Loss Reason before closing this opportunity as lost.
Set the error location to Loss Reason. If Loss_Reason__c is a text field instead of a picklist, remove TEXT() and use ISBLANK(Loss_Reason__c).
Activate rules and choose useful error locations
Keep the Active checkbox clear while you build and test a rule. After activation, the rule applies to future Opportunity creates and updates, including work performed through the user interface, imports, integrations, Flow, and API-based automation.
Choose a field-level error when one field fixes the problem. Salesforce explains the available field-level error placement options in its validation rule documentation.
Use the top-of-page location for conditions that involve several fields, record types, or a stage change. Keep error messages direct. A rep should know what to enter without opening a help ticket.
An active rule does not repair legacy opportunities. It can stop a user when they later edit one.
Test deployments, imports, and controlled bypasses
Test with a standard sales user and the same integration user that will create or update Opportunities. A rule that works in the Lightning record page can still interrupt a CSV import or an automation that updates StageName.
Run these test scenarios before activation
| Scenario | Expected result |
|---|---|
| Create an early-stage opportunity without ARR or term | Saves if no rule targets that stage |
| Move to Proposal without required SaaS fields | Save is blocked |
| Close Won with a positive amount and complete revenue data | Save succeeds |
| Close Lost without a loss reason | Save is blocked |
| Update through an import or integration with missing data | The save returns a validation error |
| Use an excluded record type or approved bypass permission | Behavior matches the intended scope |
Deploy custom fields, record types, stage values, custom permissions, permission sets, and validation rules together. A validation rule cannot compile when it references a field or permission that does not exist in the destination org.
Clean up old pipeline records before activation, or phase in requirements at future stages first. Otherwise, routine edits to old Opportunities can become blocked.
Use bypass permissions sparingly
Some integration jobs need a temporary exception for backfills or controlled data repair. Create a Custom Permission named Bypass_Opportunity_Validations, add it to a permission set, and assign that permission set only to approved users.
Salesforce documents a custom permission bypass pattern that checks whether the running user has permission to bypass the rule. For example, the Closed Lost rule can become:
AND(NOT($Permission.Bypass_Opportunity_Validations), IsClosed, NOT(IsWon), ISBLANK(TEXT(Loss_Reason__c)))
Do not use a broad bypass for every integration by default. It can create incomplete records that reports and automation will treat as valid.
Final validation checklist
Good Salesforce validation rules are narrow, readable, and tied to an actual sales decision. They should protect revenue data without turning ordinary record updates into a dead end.
Before activating a rule, confirm the following:
- The formula uses verified API names and exact stage values.
- Valid records make the formula return
FALSE. - Invalid records make the formula return
TRUE. - The error message tells the user how to fix the record.
- Record types, imports, integrations, and existing records have been tested.
- Bypass access is limited to a documented permission set.
Start with the Closed Lost reason rule in a sandbox, test it with a sales user and an import, then add stage-based ARR requirements once the team agrees on the data it needs.