Apex sharing is the programmatic creation of share records via Apex code — inserting rows into the object’s __Share table to grant specific users or groups access to specific records. It’s used when declarative sharing (OWD + role hierarchy + sharing rules + manual sharing) can’t express the rule your business needs, typically because the rule depends on data relationships, runtime conditions, or external system logic that sharing-rule criteria can’t evaluate.
Why Apex sharing exists
Every object whose OWD is Private or Public Read Only has a corresponding share object — Salesforce auto-generates it. For standard objects it’s named like AccountShare, OpportunityShare, CaseShare. For custom objects it’s MyCustomObject__Share. Every row in the share table grants one user or group access to one record at a specified access level (Read or Edit).
Apex sharing lets you insert rows into that share table from code, giving you a programmatic backdoor for sharing requirements like:
- Share an opportunity with the related Account’s account team members.
- Share a custom Project record with everyone who’s listed in a related “Stakeholders” junction object.
- Share an Order record temporarily with a user during a specific approval step.
- Share a Lead with users selected by an external matching service.
These rules can’t be expressed in standard sharing-rule criteria because they depend on related records, user lists computed at runtime, or external data.
The share record structure
A row in any *__Share table has four key fields:
| Field | What it holds |
|---|---|
| ParentId | The Id of the record being shared (e.g., the Project__c record) |
| UserOrGroupId | The Id of the user, public group, role, role+subordinates, or queue receiving access |
AccessLevel (or e.g. OpportunityAccessLevel) | Read, Edit, or All (All is reserved for the owner) |
| RowCause | Why this share exists — Manual, an Apex-defined sharing reason, or a system reason |
Sharing reasons — the key custom feature
For custom objects, you can define Apex Sharing Reasons on the object — labels that let you distinguish your code-created shares from manual shares. This matters because manual shares are removed when ownership of the record changes, but Apex-reason shares with a custom reason can persist across owner changes and be filtered/recalculated as a group.
On standard objects, you must use RowCause = Schema.OpportunityShare.RowCause.Manual (or the equivalent) — standard objects do not support custom Apex sharing reasons.
Minimal code example
public without sharing class ProjectStakeholderSharing {
public static void shareProjectWithStakeholders(Id projectId, Set<Id> userIds) {
List<Project__Share> shares = new List<Project__Share>();
for (Id uid : userIds) {
Project__Share s = new Project__Share();
s.ParentId = projectId;
s.UserOrGroupId = uid;
s.AccessLevel = 'Edit';
s.RowCause = Schema.Project__Share.RowCause.Stakeholder_Access__c;
shares.add(s);
}
Database.SaveResult[] results = Database.insert(shares, false);
// Inspect results to handle duplicate-share errors gracefully
}
}
A few production-grade notes:
- The class is declared
without sharingbecause creating share rows often requires bypassing the running user’s own sharing — the user wouldn’t necessarily have access to the records they’re sharing with others. - Use
Database.insert(shares, false)(allOrNone = false) so partial successes are allowed — duplicates raiseDUPLICATE_VALUEerrors you can swallow. - The
RowCausereferences an Apex Sharing Reason namedStakeholder_Accessdefined on the Project__c object in Setup.
When Apex sharing is the right answer
| Scenario | Right tool |
|---|---|
| Share leads in a territory with everyone in that territory | Territory management |
| Share all opportunities over $1M with the CFO group | Criteria-based sharing rule |
| Share an account with everyone in its account team | Account team (built-in) |
| Share a custom Project with the related Account’s owner | Apex sharing (no declarative path covers this) |
| Share a record temporarily during one approval step | Apex sharing triggered from the approval process |
| Share a record with an externally computed list of users | Apex sharing |
Caveats and gotchas
- Apex sharing does not apply when OWD is Public Read/Write — everyone already has access; share rows are ignored.
- Manual-reason shares are automatically deleted when record ownership changes. Custom-reason shares on custom objects are not.
- A user can have multiple share rows pointing at the same record (different reasons). Their effective access is the most permissive (Edit beats Read).
- Recalculation of shares on owner change or sharing-model change can be expensive; Apex sharing rows are part of that recalc.
- Apex sharing requires the same care around governor limits as any DML — bulkify, batch where needed.
Verified against: Apex Developer Guide — Sharing in Apex and Apex Managed Sharing. Last reviewed 2026-05-17.