SfSkills Domains Docs GitHub

Home / admin / sharing-rules

sharing-rules

Use when access must reach past the record owner's role hierarchy. Trigger keywords: sharing rule, criteria-based sharing rule, owner-based sharing rule, sharing rule recalculation, guest user sharing rule, share with a public group, SharingRules metadata. NOT for org-wide defaults - use admin/sharing-and-visibility. NOT for programmatic shares - use apex/apex-managed-sharing. NOT for subtracting access - use admin/restriction-rules.

Open the skill folder on GitHub


Sharing Rules

A sharing rule is a standing instruction to the platform: whenever a record matches this condition, write an access grant for this group. It is the workhorse declarative mechanism for granting record access sideways — to peers, to a service desk, to a partner team — without changing ownership, without a manual share per record, and without inventing a role that does not reflect the business.

This skill owns the rules themselves. The surrounding access model — org-wide defaults, the role hierarchy, manual sharing, implicit sharing, View All bypasses — belongs to admin/sharing-and-visibility, and this skill assumes that model is already decided.


Before Starting

Gather this context before designing or touching any rule:


Core Concepts

Owner-based vs criteria-based

Owner-based (SharingOwnerRule)Criteria-based (SharingCriteriaRule)
Matches onWho owns the record (sharedFrom)Field values on the record (criteriaItems + booleanFilter)
Re-evaluates whenOwnership changes, or source/target group membership changesThe criteria fields change, plus group membership changes
Typical use"Everything the West reps own goes to the Services desk""Every Account with Type = Partner goes to the channel team"
Extra required field—includeRecordsOwnedByAll, and you can't edit it after the rule is created
API version33.0+33.0+

Both extend SharingBaseRule, which carries the fields that actually decide access: accessLevel (required), sharedTo (required), label (required), an optional description of up to 1,000 characters, and — on Account rules only — accountSettings.

What a rule can and cannot grant

A sharing rule adds an access grant. It never removes one. There is no "deny" sharing rule, no ordering between rules, and no precedence: the most permissive grant on a record wins, whatever produced it. If the requirement is "these users must stop seeing records they currently see," a sharing rule is the wrong tool — see admin/restriction-rules.

Design for Read or Edit. All — Full Access, where "the specified user or group can view, edit, transfer, share, and delete the record" — appears in the enumeration inherited from the removed CriteriaBasedSharingRule type and on the share row, but it is not access a sharing rule can ask for. The Apex Developer Guide scopes it to "managed sharing" and the share-object field reference is blunter: "The All access level is an internal value and can't be granted." Account rules carry a nested accountSettings block whose caseAccessLevel, contactAccessLevel, and opportunityAccessLevel are each required and each take None, Read, or Edit — this is how one Account rule cascades to the account's children, and it is where over-sharing usually enters an org unnoticed.

Who you can share to

sharedTo names the recipient. The elements that matter in practice:

sharedTo elementGrants toNote
groupA public groupThe workhorse; see admin/queues-and-public-groups
roleUsers in one roleNarrow — no subordinates
roleAndSubordinatesInternalA role plus its internal subordinatesCurrent name after the Secure Roles rename
queueA queueSalesforce: "Applies only to lead, case, and CustomObject sharing rules"
territory / territoryAndSubordinatesTerritory membersPairs with SharingTerritoryRule
managers / managerSubordinatesA user's management chain / their subordinatesManager groups, not the role hierarchy
allInternalUsers"A group containing all internal and nonportal users"Effectively org-wide; justify it in writing
guestUserNamed guest usersSharingGuestRule only

Prefer a public group over a bare role even when the role is currently the right population. The group survives a reorg; the role reference does not.

Guest user sharing rules

SharingGuestRule (API 47.0+) is a separate array on SharingRules, not a variant of the other two. Two hard constraints come straight from the Metadata API reference: "For SharingGuestRule, the accessLevel field can be set only to Read", and includeHVUOwnedRecords is required and "you can't edit this field after the sharing rule is created." Criteria (criteriaItems, booleanFilter) arrived in API 48.0.

Everything else about unauthenticated access — which objects the guest profile can even read, what the site exposes — is admin/experience-cloud-guest-access and security/guest-user-security. Come here only for the rule itself.

The share row a rule produces

Every grant lands as a row in the object's share table: AccountShare, OpportunityShare, and for a custom object, MyCustomObject__Share. Four fields carry the meaning:

FieldHoldsNote
ParentId (custom) / AccountId etc. (standard)The shared record"This field can't be updated."
UserOrGroupIdThe recipientUser or Group Id
AccessLevel (custom) / AccountAccessLevel etc. (standard)Read, Edit, or All"The All access level is an internal value and can't be granted."
RowCauseWhy the grant exists"This field can't be updated."

RowCause is the field that makes an access audit possible. Salesforce: "when a record owner manually shares a record with a user or group, Salesforce creates a sharing row with a Manual row cause. When a sharing rule shares the record with a user or group, Salesforce creates a sharing row with a Rule row cause." The documented set also includes Owner, Team, ImplicitChild, ImplicitParent, TerritoryRule, TerritoryManual, and Territory2AssociationManual.

The consequence to internalise: RowCause = 'Rule' rows are system-maintained. "The reason determines the type of sharing, which controls who can alter the sharing record." You do not insert them, you do not delete them, and deleting one by hand is not how you revoke access — changing the rule, the criteria, or the ownership is.

-- Which grants exist on one Account, and what produced each of them
SELECT Id, UserOrGroupId, AccountAccessLevel, OpportunityAccessLevel, RowCause
FROM AccountShare
WHERE AccountId = '001XX000003DHPh'

-- Only the rule-produced grants on a custom object
SELECT Id, ParentId, UserOrGroupId, AccessLevel, RowCause
FROM Project__Share
WHERE RowCause = 'Rule'

Recalculation

Salesforce does not evaluate sharing rules at query time. "Rather than applying every sharing rule, traversing all hierarchies, and analyzing record access inheritance in real time, Salesforce calculates record access data only when configuration changes occur." Those precomputed rows are what a query joins against, which is why access is fast and why changing the model is expensive.

Recalculation is asynchronous — "automatic sharing recalculation is processed asynchronously and in parallel" — but it is not unobservable, and most advice on this topic gets that backwards. Salesforce documents both a progress view and a completion signal: "You can monitor the progress of your parallel sharing rule or organization-wide default recalculation on the Background Jobs page or view recent sharing operations on the View Setup Audit Trail page," and "You receive an email notification when the recalculation is completed for all affected objects." It is triggered by far more than editing a rule: role moves, group membership edits, queue changes, ownership transfers, and criteria-field updates all cause the platform to rewrite share rows and group maintenance tables. The architect guidance is explicit that "it can take some time to recalculate access for a large number of users, and adjust the tables that record their access rights," and that "the size and complexity of an organization's queues and hierarchies directly affect the duration of record access calculations."

While the job runs the model is partly frozen. The Security Guide documents share locks: "You can't modify the org-wide defaults when a sharing rule recalculation for any object is in progress. Similarly, you can't modify sharing rules when recalculation for an org-wide default update is in progress" — though "You can make changes to the org-wide defaults and sharing rules for other objects." That lock, not politeness, is why an impatient admin cannot simply re-edit their way out of a slow rule.

For bulk structural change there is a deliberate escape hatch: defer sharing calculation, "which allows users to defer the processing of sharing rules until after new users, rules, and other content have been loaded." It is gated by a specific permission and it suspends and resumes two distinct processes — group membership calculation and sharing rule calculation. Deferral does not make the work smaller; it batches many small recalculations into one large one you schedule.

Deep-dive on the failure modes — the unwatched recalculation, the ownership-change deletions, the criteria-churn trap — is in references/gotchas.md. Read it before you promise a business stakeholder a completion time.


Common Patterns

Pattern: Peer visibility inside a region

When to use: OWD on Opportunity is Private. Reps in one region must read each other's pipeline. The role hierarchy gives their manager visibility but gives them nothing, because hierarchy access travels up, never sideways.

How it works:

  1. Build one public group holding the region's roles (admin/queues-and-public-groups), not a list of named users.
  2. Create an owner-based rule: sharedFrom = that group, sharedTo = the same group, accessLevel = Read.
  3. New joiners inherit access the moment they land in a member role. There is no per-user step.

Why not the alternative: a criteria-based rule on a region field looks equivalent and is not. It re-evaluates when the field changes, so a mis-keyed region silently moves the record out of the group's view; and it does not follow ownership, so a transferred deal keeps matching the old region until someone edits the field.

Pattern: Field-driven access to a sensitive subset

When to use: One team should see only the records carrying a particular classification — Type = 'Partner', Stage = 'Legal Review', a compliance checkbox. Ownership is irrelevant and scattered.

How it works: a criteria-based rule with criteriaItems and, when there is more than one condition, an explicit booleanFilter. Set includeRecordsOwnedByAll deliberately — it decides "whether records owned by users who can't have an assigned role are included in the records shared," which is exactly the integration users and portal-adjacent accounts everyone forgets — and remember you cannot change it later without recreating the rule.

Why not the alternative: granting the team View All on the object is one click and is unbounded. The criteria rule is the narrow version of the same access.

Pattern: Publishing records to an Experience Cloud site

When to use: anonymous visitors must read a catalogue, a knowledge-adjacent custom object, or a public listing.

How it works: a SharingGuestRule targeting the site's guest user via sharedTo → guestUser, accessLevel = Read (the only value it accepts), with includeHVUOwnedRecords set correctly on the first attempt.

Why not the alternative: an ordinary criteria-based rule pointed at a group that happens to contain the guest user is not the same object and is the single most common way orgs accidentally over-expose data to the internet.


Decision Guidance

SituationRecommended approachReason
Access follows who owns the recordOwner-based rule, source and target as public groupsSurvives reorgs; no field to keep clean
Access follows a value on the recordCriteria-based rule with explicit booleanFilterThe record carries its own routing
Criteria field changes on most records every weekReconsider — use owner-based or a teamEach change is a recalculation event
Access must be removed from a populationadmin/restriction-rulesSharing rules only add
Recipient is "whoever is working this record"Account/Opportunity/Case teams, or apex/apex-managed-sharingNot a group the platform can name
Logic needs cross-object or computed conditionsapex/apex-managed-sharing with a custom sharing reasonCriteria evaluate fields on the record only
Anonymous site visitorsSharingGuestRule, Read onlyThe only supported guest mechanism
Manager chain, not the role hierarchysharedTo → managers / managerSubordinatesManager groups are a separate axis
Loading users, roles, groups and rules togetherDefer sharing calculation, then resume onceOne large recalculation beats hundreds of small ones
Rule count on the object is climbing every quarterConsolidate onto groups; re-examine OWD and hierarchyRule sprawl is a symptom of a wrong baseline

Metadata Shape

All rules for one object live in a single file. Retrieve the object's existing file and add to it rather than authoring the envelope by hand — a retrieved file already carries the element order the API validates against. operation takes a value from the FilterOperation enumeration: equals, notEqual, lessThan, greaterThan, lessOrEqual, greaterOrEqual, contains, notContain, startsWith, includes, excludes.

<!-- force-app/main/default/sharingRules/Account.sharingRules-meta.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<SharingRules xmlns="http://soap.sforce.com/2006/04/metadata">
    <sharingOwnerRules>
        <fullName>West_Reps_To_Services_Desk</fullName>
        <accessLevel>Read</accessLevel>
        <accountSettings>
            <caseAccessLevel>Read</caseAccessLevel>
            <contactAccessLevel>Read</contactAccessLevel>
            <opportunityAccessLevel>None</opportunityAccessLevel>
        </accountSettings>
        <description>Services desk reads accounts owned by West sales, cases and contacts included, pipeline excluded.</description>
        <label>West Reps to Services Desk</label>
        <sharedFrom>
            <group>West_Sales_Reps</group>
        </sharedFrom>
        <sharedTo>
            <group>Services_Desk</group>
        </sharedTo>
    </sharingOwnerRules>
    <sharingCriteriaRules>
        <fullName>Partner_Accounts_To_Channel</fullName>
        <accessLevel>Edit</accessLevel>
        <accountSettings>
            <caseAccessLevel>Read</caseAccessLevel>
            <contactAccessLevel>Edit</contactAccessLevel>
            <opportunityAccessLevel>Read</opportunityAccessLevel>
        </accountSettings>
        <label>Partner Accounts to Channel Team</label>
        <sharedTo>
            <group>Channel_Team</group>
        </sharedTo>
        <booleanFilter>1 AND 2</booleanFilter>
        <criteriaItems>
            <field>Type</field>
            <operation>equals</operation>
            <value>Partner</value>
        </criteriaItems>
        <criteriaItems>
            <field>Active__c</field>
            <operation>equals</operation>
            <value>true</value>
        </criteriaItems>
        <includeRecordsOwnedByAll>false</includeRecordsOwnedByAll>
    </sharingCriteriaRules>
</SharingRules>

The manifest addresses the concrete rule types, not the SharingRules container. This is the sample the Metadata API Developer Guide ships for "retrieving a specific criteria-based sharing rule for the lead object, retrieving all ownership-based sharing rules for all objects, and retrieving all territory-based sharing rules for the account object":

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>Lead.testShareRule</members>
        <name>SharingCriteriaRule</name>
    </types>
    <types>
        <members>*</members>
        <name>SharingOwnerRule</name>
    </types>
    <types>
        <members>Account.*</members>
        <name>SharingTerritoryRule</name>
    </types>
    <version>33.0</version>
</Package>

  1. Confirm the baseline. Read the object's OWD and the role hierarchy first. If OWD is Public Read/Write, stop — no rule is needed. If the hierarchy already delivers the access, stop — a rule is redundant weight on every future recalculation.
  2. Name the population as a group. Create or reuse a public group. Reject the design if the audience cannot be expressed as a group, a role, a queue, a territory, or a guest user; route it to teams or apex/apex-managed-sharing.
  3. Choose the rule type from the requirement's grammar. Ownership sentence → owner-based. Field sentence → criteria-based. Write the chosen accessLevel, and for Account also the three accountSettings values, before touching Setup.
  4. Build it as metadata. Retrieve the object's existing <Object>.sharingRules-meta.xml, add the rule, and deploy. Set includeRecordsOwnedByAll (or includeHVUOwnedRecords for guest rules) correctly the first time — neither is editable afterwards.
  5. Run the checker. python3 skills/admin/sharing-rules/scripts/check_sharing_rules.py --manifest-dir <metadata-dir> catches missing required fields, booleanFilter indexes that reference criteria that do not exist, guest rules above Read, and org-wide recipients.
  6. Verify against the share table, not the UI. Watch Setup → Background Jobs until the recalculation clears — or wait for the completion email — then query the object's share object filtered to RowCause = 'Rule' and confirm the expected UserOrGroupId rows exist at the expected access level. Then log in as a target user and open a record that should not match.
  7. Plan the recalculation before any bulk change. For reorgs, mass ownership transfers, or user loads, use deferred sharing calculation, resume once, and treat the resume as a scheduled maintenance window rather than an instant operation.

Review Checklist


Salesforce-Specific Gotchas

Summaries only — the mechanism, the trigger conditions, and the fix for each are in references/gotchas.md.

  1. Saving a rule is not granting access. Recalculation is asynchronous, so "I created it and nothing happened" is usually a timing report, not a bug — track it on Setup → Background Jobs rather than on the rule page.
  2. Changing the owner rewrites the record's grants. Manual shares are deleted outright and owner-based rules re-evaluate from the new owner's groups — access can silently disappear from a transfer nobody connected to sharing.
  3. includeRecordsOwnedByAll is write-once. Getting it wrong means deleting and recreating the rule, which is a fresh recalculation on the whole object.
  4. Criteria on a churning field turns every record edit into sharing maintenance. The rule is correct and the org is slow.
  5. Account rules cascade to Cases, Contacts, and Opportunities through accountSettings, which is how a "read the account" request becomes pipeline exposure.
  6. RowCause = 'Rule' rows cannot be deleted to revoke access — they are system-maintained and will be rewritten.
  7. Sharing to a queue is object-limited — Salesforce documents queue as applying "only to lead, case, and CustomObject sharing rules."

Output Artifacts

ArtifactDescription
Rule design recordRule type, source group, target group, access level, criteria fields, and the reason for each
<Object>.sharingRules-meta.xmlDeployable rule definitions for the object
package.xml entriesSharingCriteriaRule / SharingOwnerRule / SharingTerritoryRule members for retrieve and deploy
Verification SOQLShare-object query filtered to RowCause = 'Rule', plus a negative test
Recalculation planWhich structural changes are batched, when deferral is enabled, and who owns the resume
Checker outputMalformed, non-deployable, or over-broad rules found in the metadata

Package files