Why RestrictionRule Solves the “Over-Shared Parent Record” Problem
For decades, Salesforce’s record-level security model was strictly additive: Organization-Wide Defaults (OWD) set the baseline, and Role Hierarchies, SharingRules, Teams, and Manual Sharing opened access up. However, certain child objects—such as Task, Event, Contract, Quote, or Master-Detail custom objects—inherit visibility from their parent Account or Case, making it impossible to hide sensitive HR tasks or executive contracts using standard Sharing Rules.
The RestrictionRule metadata type (restrictionRules/<Name>.rule-meta.xml) introduces subtractive filtering (enforcementType: Restrict) as well as default view scoping (enforcementType: Scoping).
Key Metadata Fields on RestrictionRule
| Field | Type | Required | Description |
|---|---|---|---|
enforcementType |
EnforcementType |
Yes | Restrict (hard security restriction rule) or Scoping (productivity scoping rule). |
targetEntity |
string |
Yes | Object API name (Task, Event, Contract, Quote, Account, or Custom_Object__c). |
userCriteria |
string |
Yes | Formula/criteria evaluating which logged-in users the rule applies to (e.g., $Permission.Contractor_Restricted_Access = true). |
recordFilter |
string |
Yes | Criteria evaluating which records those users are permitted to see (e.g., Is_Confidential__c = false). |
active |
boolean |
Yes | Enables (true) or disables (false) the rule. |
masterLabel |
string |
Yes | Human-readable rule name in Object Manager. |
version |
int |
Yes | Rule schema version (1). |
Real-World XML Examples
1. Restriction Rule Using a Custom Permission (restrictionRules/Hide_Executive_Legal_Tasks.rule-meta.xml)
Tip
Always Use $Permission Instead of Hardcoded ProfileId in userCriteria: Official documentation examples often show $User.ProfileId = '00e...', which breaks CI/CD deployments across Sandboxes and Scratch Orgs. Binding userCriteria to a CustomPermission makes your RestrictionRule 100% portable!
<?xml version="1.0" encoding="UTF-8"?>
<RestrictionRule xmlns="http://soap.sforce.com/2006/04/metadata">
<active>true</active>
<description>Restricts external support contractors to viewing only non-confidential Tasks assigned to their division.</description>
<enforcementType>Restrict</enforcementType>
<masterLabel>Hide Executive Legal Tasks</masterLabel>
<recordFilter>Is_Executive_Confidential__c = false</recordFilter>
<targetEntity>Task</targetEntity>
<userCriteria>$Permission.External_Support_Contractor = true</userCriteria>
<version>1</version>
</RestrictionRule>2. Scoping Rule on Account (restrictionRules/Scope_Accounts_By_User_Division.rule-meta.xml)
<?xml version="1.0" encoding="UTF-8"?>
<RestrictionRule xmlns="http://soap.sforce.com/2006/04/metadata">
<active>true</active>
<description>Scopes default Account list views and lookups to match the logged-in seller division.</description>
<enforcementType>Scoping</enforcementType>
<masterLabel>Scope Accounts By User Division</masterLabel>
<recordFilter>Sales_Division__c = $User.Division</recordFilter>
<targetEntity>Account</targetEntity>
<userCriteria>$User.IsActive = true</userCriteria>
<version>1</version>
</RestrictionRule>Querying RestrictionRule via Tooling API
You can audit all active Restriction Rules and Scoping Rules across your org by querying RestrictionRule via the Tooling API.
SELECT Id, DeveloperName, MasterLabel, EntityDefinition.QualifiedApiName,
EnforcementType, Active, UserCriteria, RecordFilter, LastModifiedDate
FROM RestrictionRule
ORDER BY EntityDefinition.QualifiedApiName, EnforcementType, DeveloperNameSenior Architect Best Practices & Performance Rules
- Index Fields Referenced in
recordFilter: Because Salesforce appendsrecordFilterto every SOQL query, List View, Report, and Lookup executed by matching users, any custom field used insiderecordFiltermust be indexed (marked External ID or custom indexed) on large-data-volume (LDV) objects to prevent query timeouts. - One Active Rule Evaluated Per User: Ensure that a single user only matches one active
RestrictionRuleon a given object. If multiple active rules match the same user on the same object, Salesforce only evaluates one of them (non-deterministically).
Frequently asked questions
What is the difference between Restrict and Scoping on the RestrictionRule metadata type?
When enforcementType is Restrict, the rule acts as a hard security filter on top of OWD and sharing rules, hiding non-matching records in SOQL, reports, and list views. When enforcementType is Scoping, it pre-filters default record views without revoking underlying read access.
Which standard and custom objects support Restriction Rules?
Restriction rules (enforcementType set to Restrict) support custom objects, external objects, Contract, Event, Quote, Task, TimeSheet, and TimeSheetEntry. Scoping rules support custom objects plus Account, Case, Contact, Event, Lead, Opportunity, and Task.
Why do RestrictionRule deployments fail when userCriteria references a ProfileId or UserRoleId?
Hardcoded 15- or 18-character ProfileId or UserRoleId values in userCriteria are org-specific. Best practice is to reference a CustomPermission ($Permission.Restrict_Competitor_Deals = true) or a custom field on the User object ($User.Department = 'Retail').
How many active Restriction Rules can you create per object?
Enterprise and Performance Editions allow up to 2 active Restriction Rules (and 2 active Scoping Rules) per object, while Unlimited and Developer Editions allow up to 5 active rules per object.
Can you query RestrictionRule records using the Tooling API?
Yes. Users with the View Restriction and Scoping Rules permission can query the RestrictionRule object via the Tooling API to inspect DeveloperName, EntityDefinitionId, EnforcementType, RecordFilter, UserCriteria, and Active status.
Comments
Questions, corrections and tips are welcome. Comments are GitHub Discussions, so you sign in with GitHub.