agentleFS
jiten-singh-shahijiten-singh-shahi/salesforce-claude-code/.cursor/skills/sf-approval-processes/SKILL.md

sf-approval-processes

Skill15 starsChanged 7 months agoMITView on GitHub

Use when designing Salesforce Apex approval processes, multi-step approvals, or Flow-integrated submissions. Do NOT use for general Apex or Flow-only work.

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 3 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Unanimous: All assigned approvers must approve

    line 62
  2. Distinctive:

    Use the Submit for Approval action element: specify the record ID, optionally the approval process name, submitter, and comments.

    line 178
  3. Distinctive:

    Use Queues as approvers so any queue member can approve

    line 211

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Salesforce Approval Processes

@../_reference/APPROVAL_PROCESSES.md

When to Use

  • Designing multi-step approval workflows (discounts, expenses, contracts)
  • Building Apex-driven approval submissions or programmatic approvals/rejections
  • Integrating approval logic with Flows (Spring '26 Integrated Approval Screen component)
  • Troubleshooting approval routing, locked records, or missing approval history
  • Migrating from email-based approvals to in-app approval workflows

Approval Process Architecture

┌─────────────────────────────────────────────────────┐
│                 Approval Process                     │
├─────────────────────────────────────────────────────┤
│  Entry Criteria     │ WHO can submit? WHEN?          │
│  Initial Submitter  │ Record owner, specific users   │
├─────────────────────────────────────────────────────┤
│  Step 1: Manager    │ Approver: Manager field         │
│  Step 2: VP         │ Approver: Related user field    │
│  Step 3: Finance    │ Approver: Queue                 │
├─────────────────────────────────────────────────────┤
│  Initial Actions    │ Lock record, set Status         │
│  Approval Actions   │ Unlock, update field, email     │
│  Rejection Actions  │ Unlock, set Status to Rejected  │
│  Recall Actions     │ Unlock, clear approval fields   │
└─────────────────────────────────────────────────────┘

Entry Criteria

Amount__c > 10000 AND Status__c = 'Draft'
RecordType.Name = 'Enterprise' AND Discount__c > 20

Formula:
AND(Amount__c > 10000, ISPICKVAL(Status__c, 'Draft'), NOT(ISBLANK(OwnerId)))

Approval Steps

PropertyOptions
ApproverUser field, Manager field, Queue, Related user
CriteriaAll records OR filter criteria (step-specific)
Reject behaviorFinal rejection OR go to previous step
UnanimityAll must approve OR first response

Parallel Approvals

  • Unanimous: All assigned approvers must approve
  • First Response: First approver's decision applies
  • For true parallel independent steps (Legal AND Finance simultaneously), use multiple processes or custom Apex

Approval Actions

Initial Submission Actions

  • Field Update: Status__c = "Pending Approval"
  • Email Alert: Notify approver(s)
  • Record Lock: Prevent edits during approval
  • Outbound Message: Notify external system

Final Approval Actions

  • Field Update: Status__c = "Approved"
  • Email Alert: Notify submitter
  • Record Unlock: Allow edits again

Final Rejection Actions

  • Field Update: Status__c = "Rejected"
  • Email Alert: Notify submitter with rejection reason
  • Record Unlock

Apex Approval Processing

Submitting a Record for Approval

public class ApprovalService {
    public static void submitForApproval(Id recordId, String comments) {
        Approval.ProcessSubmitRequest request = new Approval.ProcessSubmitRequest();
        request.setObjectId(recordId);
        request.setSubmitterId(UserInfo.getUserId());
        request.setComments(comments);

        Approval.ProcessResult result = Approval.process(request);
        if (result.isSuccess()) {
            System.debug('Submitted. Instance ID: ' + result.getInstanceId());
        } else {
            for (Database.Error err : result.getErrors()) {
                System.debug(LoggingLevel.ERROR, 'Failed: ' + err.getMessage());
            }
        }
    }
}

Approving or Rejecting Programmatically

public static void approveRecord(Id workItemId, String comments) {
    Approval.ProcessWorkitemRequest request = new Approval.ProcessWorkitemRequest();
    request.setWorkitemId(workItemId);
    request.setAction('Approve');
    request.setComments(comments);

    Approval.ProcessResult result = Approval.process(request);
    if (!result.isSuccess()) {
        throw new ApprovalException('Approval failed: ' + result.getErrors());
    }
}

public static void rejectRecord(Id workItemId, String comments) {
    Approval.ProcessWorkitemRequest request = new Approval.ProcessWorkitemRequest();
    request.setWorkitemId(workItemId);
    request.setAction('Reject');
    request.setComments(comments);

    Approval.ProcessResult result = Approval.process(request);
    if (!result.isSuccess()) {
        throw new ApprovalException('Rejection failed: ' + result.getErrors());
    }
}

Querying Approval Status

// Pending work items for current user
List<ProcessInstanceWorkitem> pendingItems = [
    SELECT Id, ProcessInstance.TargetObjectId, ProcessInstance.Status,
           ProcessInstance.TargetObject.Name, CreatedDate
    FROM ProcessInstanceWorkitem
    WHERE ActorId = :UserInfo.getUserId()
    ORDER BY CreatedDate DESC
];

// Approval history for a record
List<ProcessInstanceStep> history = [
    SELECT StepStatus, Comments, Actor.Name, CreatedDate
    FROM ProcessInstanceStep
    WHERE ProcessInstance.TargetObjectId = :recordId
    ORDER BY CreatedDate ASC
];

// Active work item for a specific record
ProcessInstanceWorkitem workItem = [
    SELECT Id FROM ProcessInstanceWorkitem
    WHERE ProcessInstance.TargetObjectId = :recordId
      AND ProcessInstance.Status = 'Pending'
    LIMIT 1
];

Flow Integration

Submitting for Approval from Flow

Use the Submit for Approval action element: specify the record ID, optionally the approval process name, submitter, and comments.

Spring '26 — Integrated Approval Screen Component

Screen Flows can include an Integrated Approval component for in-flow approve/reject:

Screen Flow:
  1. Show Record Details
  2. Integrated Approval Component (history, Approve/Reject buttons, comments)
  3. Decision: Check outcome
  4. Update Records based on outcome

Dynamic Approvers via Flow

Autolaunched Flow (invoked by Approval Process):
  1. Get Records: record's Region__c and Amount__c
  2. Decision: Route by amount and region
     - > 500K → VP Finance
     - > 100K → Regional Manager
     - Otherwise → Direct Manager
  3. Return approver User ID

Design Patterns

Delegation and Backup Approvers

  • Configure Delegated Approvers in user settings for vacation coverage
  • Use Queues as approvers so any queue member can approve
  • Set approval step timeout for auto-escalation

Approval History Tracking

public static List<Map<String, Object>> getApprovalHistory(Id recordId) {
    List<Map<String, Object>> trail = new List<Map<String, Object>>();
    for (ProcessInstance pi : [
        SELECT Id, Status, CreatedDate,
            (SELECT StepStatus, Comments, Actor.Name, CreatedDate
             FROM StepsAndWorkitems ORDER BY CreatedDate)
        FROM ProcessInstance
        WHERE TargetObjectId = :recordId
        ORDER BY CreatedDate DESC
    ]) {
        for (ProcessInstanceHistory step : pi.StepsAndWorkitems) {
            trail.add(new Map<String, Object>{
                'status' => step.StepStatus,
                'approver' => step.Actor.Name,
                'comments' => step.Comments,
                'date' => step.CreatedDate
            });
        }
    }
    return trail;
}

Testing Approval Processes

These tests require an approval process to be deployed in the org's metadata. Approval processes cannot be created programmatically in tests.

@isTest
static void testApprovalSubmission() {
    Account testAccount = new Account(Name = 'Test Account', AnnualRevenue = 50000);
    insert testAccount;

    Test.startTest();
    Approval.ProcessSubmitRequest request = new Approval.ProcessSubmitRequest();
    request.setObjectId(testAccount.Id);
    request.setSubmitterId(UserInfo.getUserId());
    Approval.ProcessResult result = Approval.process(request);
    Test.stopTest();

    System.assert(result.isSuccess(), 'Approval submission should succeed');
    System.assertEquals('Pending', result.getInstanceStatus());
}

@isTest
static void testApprovalProcess() {
    Account testAccount = new Account(Name = 'Test', AnnualRevenue = 50000);
    insert testAccount;

    Approval.ProcessSubmitRequest submitReq = new Approval.ProcessSubmitRequest();
    submitReq.setObjectId(testAccount.Id);
    Approval.ProcessResult submitResult = Approval.process(submitReq);

    Id workItemId = submitResult.getNewWorkitemIds()[0];

    Approval.ProcessWorkitemRequest approveReq = new Approval.ProcessWorkitemRequest();
    approveReq.setWorkitemId(workItemId);
    approveReq.setAction('Approve');
    approveReq.setComments('Looks good');

    Test.startTest();
    Approval.ProcessResult approveResult = Approval.process(approveReq);
    Test.stopTest();

    System.assert(approveResult.isSuccess());
    System.assertEquals('Approved', approveResult.getInstanceStatus());
}

Metadata Deployment

approvalProcesses/
  Discount_Approval.approvalProcess-meta.xml

Include in package.xml:

<types>
    <members>Opportunity.Discount_Approval</members>
    <name>ApprovalProcess</name>
</types>

Approval processes reference users, queues, and email templates. Ensure dependencies exist in the target org before deployment.


Related

  • Constraints: sf-apex-constraints — Governor limits and Apex safety rules

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.