.cursor/skills/sf-flow-development/SKILL.md
Salesforce Flow development — flow types, patterns, bulkification, error handling, testing, subflows, Flow vs Apex. Use when building or reviewing Flows. Do NOT use for pure Apex or Platform Event architecture.
npx skillsauth add jiten-singh-shahi/salesforce-claude-code sf-flow-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Procedures for building, testing, and maintaining Salesforce Flows. Flow type details, governor limits, bulkification rules, and the Flow vs Apex decision matrix live in the reference file.
@../_reference/FLOW_PATTERNS.md
Flow: ACC_SetAccountPriority (Before Save, Account, before insert OR before update)
Entry Condition: {!$Record.AnnualRevenue} >= 1000000
Steps:
1. Assignment: {!$Record.Priority__c} = "High"
2. Assignment: {!$Record.Tier__c} = "Enterprise"
Notes:
- No Get Records, Update Records, or Create Records elements allowed
- No DML of any kind
- Very fast — runs in memory before DB write
Flow: OPP_CreateNegotiationTask (After Save, Opportunity, after update)
Entry Condition: {!$Record.StageName} = "Negotiation"
AND {!$Record.StageName} <> {!$Record__Prior.StageName}
Steps:
1. Get Records: User WHERE Id = {!$Record.OwnerId}
2. Create Records: Task (
Subject = "Review negotiation checklist"
WhatId = {!$Record.Id}
OwnerId = {!$Record.OwnerId}
ActivityDate = TODAY() + 3
Priority = "High"
)
Need to update the SAME record's fields?
-> Before Save (no DML counted, faster)
Note: Before Save flows cannot use Get Records or DML elements,
but CAN access parent fields via cross-object formula references
(e.g., {!$Record.Account.Name}).
Need to create/update OTHER records?
-> After Save (can do DML on other objects)
Need to send emails or call external services?
-> After Save (outbound actions need committed record)
BAD:
Loop: For each Contact in {!Contacts}
|
+-> Update Records: Contact <- DML inside loop = 1 DML per record
GOOD:
Loop: For each Contact in {!Contacts}
|
+-> Assignment: Add to contactsToUpdate collection
Update Records: {!contactsToUpdate} <- Single DML for entire collection
BAD:
Loop: For each Opportunity in {!Opportunities}
|
+-> Get Records: Account WHERE Id = {!loopVar.AccountId} <- SOQL per record
GOOD:
Get Records: Account WHERE Id IN {!opportunityAccountIds} <- single query
Loop: For each Opportunity in {!Opportunities}
|
+-> (Use data from pre-fetched collection, no SOQL)
Heap warning: If pre-fetch returns 10K+ records, the collection may
exceed the heap size limit (see @../_reference/GOVERNOR_LIMITS.md). Filter aggressively, or move to Batch Apex.
Parent Flow: SCR_CustomerOnboarding (Screen Flow)
|
+-- Subflow: VAL_ValidateAddress (Autolaunched)
| Input: {!streetAddress}, {!city}, {!state}
| Output: {!isValidAddress}, {!normalizedAddress}
|
+-- Subflow: INT_CreateCRMAccount (Autolaunched)
| Input: {!accountData}
| Output: {!newAccountId}
|
+-- Subflow: NOT_SendWelcomeEmail (Autolaunched)
Input: {!accountId}, {!contactEmail}
Variables passed between parent and subflow must be marked as available for input/output in the subflow definition.
Every element that can fail should have a fault connector.
[Update Records: Update Account]
|
+-(SUCCESS)-> [Next Step]
|
+-(FAULT)-> [Assignment: errorMessage =
"Failed: " + {!$Flow.FaultMessage}]
|
+-> [Screen: Display Error to User]
OR
+-> [Create Records: Error_Log__c]
OR
+-> [Custom Notification: Notify Admin]
Element: Get Records
Object: Contact
Filter: AccountId = {!$Record.Id} AND IsActive__c = true
Store All Records: No (when you need just one) / Yes (for a collection)
Tips:
- Add filter conditions to reduce records returned
- Select only the fields you need
- Use "Only the first record" when you need a single result
- Filter on indexed fields when possible (Id, OwnerId, ExternalId__c)
Formula Resource:
Name: FormattedMessage
Data Type: Text
Value: {!$Label.Welcome_Message} & " " & {!ContactRecord.FirstName}
Get Records:
Object: Service_Config__mdt
Filter: DeveloperName = "Production"
Fields: Endpoint_URL__c, Timeout_Ms__c
@IsTest
public class OppNegotiationFlowTest {
@TestSetup
static void setup() {
Account acc = TestDataFactory.createAccount('Test Account');
insert acc;
}
@IsTest
static void testNegotiationTask_stageChanged_createsTask() {
Account acc = [SELECT Id FROM Account LIMIT 1];
Opportunity opp = new Opportunity(
Name = 'Test Opp',
AccountId = acc.Id,
StageName = 'Prospecting',
CloseDate = Date.today().addDays(30),
Amount = 50000
);
insert opp;
Test.startTest();
opp.StageName = 'Negotiation';
update opp;
Test.stopTest();
List<Task> tasks = [SELECT Subject, Priority
FROM Task WHERE WhatId = :opp.Id];
System.assertEquals(1, tasks.size());
System.assertEquals('Review negotiation checklist', tasks[0].Subject);
}
@IsTest
static void testNegotiationTask_bulk_createsTasksForAll() {
Account acc = [SELECT Id FROM Account LIMIT 1];
List<Opportunity> opps = new List<Opportunity>();
for (Integer i = 0; i < 200; i++) {
opps.add(new Opportunity(
Name = 'Bulk Opp ' + i,
AccountId = acc.Id,
StageName = 'Prospecting',
CloseDate = Date.today().addDays(30),
Amount = 1000 * i
));
}
insert opps;
Test.startTest();
for (Opportunity opp : opps) { opp.StageName = 'Negotiation'; }
update opps;
Test.stopTest();
Integer taskCount = [SELECT COUNT() FROM Task WHERE WhatId IN :opps];
System.assertEquals(200, taskCount);
}
}
Five native Screen Flow components that eliminate the need for custom LWC in common patterns:
| Component | Best For | |-----------|----------| | Kanban Board | Stage/status reassignment wizards, visual prioritization | | Message | Confirmation prompts, warnings before destructive steps | | File Preview | Document review steps in approval flows | | Integrated Approval | Wizard-based approval flows where reps submit and track | | Editable Data Table | Mass-edit child records within a guided wizard |
Record-Triggered Flows now support ContentVersion as a triggering object.
Object: ContentVersion
Trigger: A record is created or updated
Entry Conditions:
- {!$Record.FileExtension} = 'pdf'
- {!$Record.Title} Does NOT Contain 'DRAFT'
Use cases:
- Auto-tag documents based on filename patterns
- Notify a team when a contract document is uploaded
- Create audit log when a sensitive file type is uploaded
Orchestration Flows manage long-running, multi-step processes spanning hours or days.
Orchestration Flow: Employee_Onboarding
+-- Stage 1: "System Setup"
| +-- Background Step: Create user account (Autolaunched Flow)
| +-- Background Step: Provision email (Autolaunched Flow)
+-- Stage 2: "HR Review"
| +-- Interactive Step: Complete onboarding form (Screen Flow -> HR Manager)
+-- Stage 3: "Equipment & Access"
| +-- Interactive Step: Order equipment (Screen Flow -> IT Team queue)
| +-- Background Step: Grant system permissions (Autolaunched Flow)
+-- Stage 4: "Orientation"
+-- Interactive Step: Schedule orientation (Screen Flow -> Employee)
Each Flow has:
- Multiple versions (v1, v2, v3...)
- Only ONE active version at a time
- Inactive versions can be tested without activating
Deployment:
- Deploying creates a new version
- Set status: Active in Flow metadata to auto-activate:
<status>Active</status>
sf project deploy start \
--metadata "Flow:OPP_CreateNegotiationTask" \
--target-org myOrg
1. IDENTIFY — List all active Process Builders
sf data query -q "SELECT Id, MasterLabel, ProcessType FROM Flow
WHERE ProcessType = 'Workflow' AND Status = 'Active'" --json
2. ANALYZE — Document trigger object, criteria, and actions
3. CREATE — Build equivalent Record-Triggered Flow:
- Before Save Flow for field updates
- After Save Flow for create records, email alerts
4. TEST — Deploy to sandbox, test with bulk data (200+ records)
5. DEACTIVATE — Turn off Process Builder, monitor 1-2 weeks
6. CLEANUP — Delete via destructiveChanges.xml
sf-flow-agent -- for interactive, in-depth guidancedevelopment
Update Salesforce platform reference docs with latest release features and deprecation announcements. Use when SessionStart hook warns docs are outdated or a new Salesforce release has shipped. Do NOT use for Apex or LWC development.
development
Use when syncing documentation after Salesforce Apex code changes. Update README, API docs, and deploy metadata references to match the current org codebase.
development
Use when managing context during long Salesforce Apex development sessions. Suggests manual compaction at logical intervals to preserve deploy and org context across phases.
tools
Visualforce development — pages, controllers, extensions, ViewState, JS Remoting, LWC migration. Use when maintaining VF pages, building PDFs, or planning VF-to-LWC migration. Do NOT use for LWC, Aura, or Flow.