Creates and validates Salesforce flows using best practices and metadata standards. Expert Flow Builder with deep knowledge of bulkification, Winter '26 (API 62.0), and 110-point scoring validation. Supports 7 flow types with strict mode enforcement.
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Expert Salesforce Flow Builder with deep knowledge of best practices, bulkification, and Winter '26 (API 62.0) metadata. Create production-ready, performant, secure, and maintainable flows.
Validate Flow XML before deployment:
# Path to validation script
python3 ~/.claude/plugins/marketplaces/sf-skills/sf-flow-builder/hooks/scripts/validate_flow.py <flow-file.xml>
# Example
python3 ~/.claude/plugins/marketplaces/sf-skills/sf-flow-builder/hooks/scripts/validate_flow.py \
force-app/main/default/flows/Auto_Lead_Assignment.flow-meta.xml
Scoring: 110 points across 6 categories. Minimum 88 (80%) for deployment.
sf-metadata β sf-flow β sf-devops-architect β sf-data (you are here: sf-flow)
β οΈ Flow references custom object/fields? Create with sf-metadata FIRST. Deploy objects BEFORE flows.
β οΈ ALL deployments MUST go through sf-devops-architect sub-agent (which delegates to sf-deploy).
See shared/docs/orchestration.md (project root) for details.
| Insight | Details |
|---|---|
| Before vs After Save | Before-Save: same-record updates (no DML), validation. After-Save: related records, emails, callouts |
| Test with 251 | Batch boundary at 200. Test 251+ records for governor limits, N+1 patterns, bulk safety |
| $Record context | Single-record, NOT a collection. Platform handles batching. Never loop over $Record |
Use AskUserQuestion to gather:
Then:
Glob: pattern="**/*.flow-meta.xml"docs/subflow-library.md (in sf-flow folder)docs/governance-checklist.md (in sf-flow folder)Select template:
| Flow Type | Template File |
|---|---|
| Screen | screen-flow-template.xml |
| Record-Triggered | record-triggered-*.xml |
| Platform Event | platform-event-flow-template.xml |
| Autolaunched | autolaunched-flow-template.xml |
| Scheduled | scheduled-flow-template.xml |
Template Path Resolution (try in order):
~/.claude/plugins/marketplaces/sf-skills/sf-flow/templates/[template].xml[project-root]/sf-flow/templates/[template].xmlExample: Read: ~/.claude/plugins/marketplaces/sf-skills/sf-flow/templates/record-triggered-flow-template.xml
Naming Convention (Recommended Prefixes):
| Flow Type | Prefix | Example |
|---|---|---|
| Record-Triggered (After) | Auto_ | Auto_Lead_Assignment, Auto_Account_Update |
| Record-Triggered (Before) | Before_ | Before_Lead_Validate, Before_Contact_Default |
| Screen Flow | Screen_ | Screen_New_Customer, Screen_Case_Intake |
| Scheduled | Sched_ | Sched_Daily_Cleanup, Sched_Weekly_Report |
| Platform Event | Event_ | Event_Order_Completed |
| Autolaunched | Sub_ or Util_ | Sub_Send_Email, Util_Validate_Address |
Format: [Prefix]_Object_Action using PascalCase (e.g., Auto_Lead_Priority_Assignment)
Screen Flow Button Config (CRITICAL):
| Screen | allowBack | allowFinish | Result |
|---|---|---|---|
| First | false | true | "Next" only |
| Middle | true | true | "Previous" + "Next" |
| Last | true | true | "Finish" |
Rule: allowFinish="true" required on all screens. Connector present β "Next", absent β "Finish".
Orchestration: For complex flows (multiple objects/steps), suggest Parent-Child or Sequential pattern.
docs/xml-gotchas.md (in sf-flow) and docs/orchestration-guide.md (in sf-flow)Create flow file:
mkdir -p force-app/main/default/flows
Write: force-app/main/default/flows/[FlowName].flow-meta.xml
Populate template: Replace placeholders, API version: 62.0
CRITICAL Requirements:
<bulkSupport> (removed API 60.0+)Run Enhanced Validation (automatic via plugin hooks): The plugin automatically validates Flow XML files when written. Manual validation:
python3 ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/validate_flow.py force-app/main/default/flows/[FlowName].flow-meta.xml
Validation (STRICT MODE):
New v2.0.0 Validations:
storeOutputAutomatically detection (data leak prevention)Run Simulation (REQUIRED for record-triggered/scheduled):
python3 ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/simulate_flow.py force-app/main/default/flows/[FlowName].flow-meta.xml --test-records 200
If simulation fails: STOP and fix before proceeding.
Validation Report Format (6-Category Scoring 0-110):
Score: 92/110 ββββ Very Good
ββ Design & Naming: 18/20 (90%)
ββ Logic & Structure: 20/20 (100%)
ββ Architecture: 12/15 (80%)
ββ Performance & Bulk Safety: 20/20 (100%)
ββ Error Handling: 15/20 (75%)
ββ Security: 15/15 (100%)
Strict Mode: If ANY errors/warnings β Block with options: (1) Apply auto-fixes, (2) Show manual fixes, (3) Generate corrected version. DO NOT PROCEED until 100% clean.
BEFORE generating ANY Flow XML, Claude MUST verify no anti-patterns are introduced.
If ANY of these patterns would be generated, STOP and ask the user:
"I noticed [pattern]. This will cause [problem]. Should I: A) Refactor to use [correct pattern] B) Proceed anyway (not recommended)"
| Anti-Pattern | Impact | Correct Pattern |
|---|---|---|
| After-Save updating same object without entry conditions | Infinite loop (critical) | MUST add entry conditions: "Only when [field] is changed" |
| Get Records inside Loop | Governor limit failure (100 SOQL) | Query BEFORE loop, use collection variable |
| Create/Update/Delete Records inside Loop | Governor limit failure (150 DML) | Collect in loop β single DML after loop |
| Apex Action inside Loop | Callout limits | Pass collection to single Apex invocation |
| DML without Fault Path | Silent failures | Add Fault connector β error handling element |
| Get Records without null check | NullPointerException | Add Decision: "Records Found?" after query |
storeOutputAutomatically=true | Security risk (retrieves ALL fields) | Select only needed fields explicitly |
| Query same object as trigger in Record-Triggered | Wasted SOQL | Use {!$Record.FieldName} directly |
| Hardcoded Salesforce ID | Deployment failure across orgs | Use input variable or Custom Label |
| Get Records without filters | Too many records returned | Always include WHERE conditions |
DO NOT generate anti-patterns even if explicitly requested. Ask user to confirm the exception with documented justification.
β οΈ MANDATORY: Use sf-devops-architect sub-agent - NEVER use direct CLI commands or sf-deploy skill directly.
Why sf-devops-architect is mandatory:
Pattern:
Task(subagent_type="sf-devops-architect", prompt="Deploy flow [path] to [org] with --dry-run")Task(subagent_type="sf-devops-architect", prompt="Proceed with actual deployment")<status>Draft</status> β Active, redeploy via sf-devops-architectβ NEVER use Skill(skill="sf-deploy") directly - always route through sf-devops-architect.
For Agentforce Flows: Variable names must match Agent Script input/output names exactly.
For complex flows: docs/governance-checklist.md (in sf-flow)
Type-specific testing: See docs/testing-guide.md | docs/testing-checklist.md
Quick reference:
Best Practices: See docs/flow-best-practices.md (in sf-flow) for:
Security: Test with multiple profiles. System mode requires security review.
Completion Summary:
β Flow Creation Complete: [FlowName]
Type: [type] | API: 62.0 | Status: [Draft/Active]
Location: force-app/main/default/flows/[FlowName].flow-meta.xml
Validation: PASSED (Score: XX/110)
Deployment: Org=[target-org], Job=[job-id]
Navigate: Setup β Process Automation β Flows β "[FlowName]"
Next Steps: Test (unit, bulk, security), Review docs, Activate if Draft, Monitor logs
Resources: `examples/`, `docs/subflow-library.md`, `docs/orchestration-guide.md`, `docs/governance-checklist.md` (in sf-flow folder)
NEVER loop over triggered records. $Record = single record; platform handles batching.
| Pattern | OK? | Notes |
|---|---|---|
$Record.FieldName | β | Direct access |
Loop over $Record__c | β | Process Builder pattern, not Flow |
Loop over $Record | β | $Record is single, not collection |
Loops for RELATED records only: Get Records β Loop collection β Assignment β DML after loop
recordLookups cannot query Parent.Field (e.g., Manager.Name). Solution: Two Get Records - child first, then parent by Id.
| Element | Recommendation | Why |
|---|---|---|
getFirstRecordOnly | Set to true for single-record queries | Avoids collection overhead |
storeOutputAutomatically | Set to false, use outputReference | Prevents data leaks, explicit variable |
assignNullValuesIfNoRecordsFound | Set to false | Preserves previous variable value |
faultConnector | Always include | Handle query failures gracefully |
filterLogic | Use and for multiple filters | Clear filter behavior |
All elements of the same type MUST be grouped together. Do NOT scatter elements across the file.
Complete alphabetical order:
apiVersion β assignments β constants β decisions β description β environments β
formulas β interviewLabel β label β loops β processMetadataValues β processType β
recordCreates β recordDeletes β recordLookups β recordUpdates β runInMode β
screens β start β status β subflows β textTemplates β variables β waits
Common Mistake: Adding an assignment near related logic (e.g., after a loop) when other assignments exist earlier.
var_ Regular variables (e.g., var_AccountName)col_ Collections (e.g., col_ContactIds)rec_ Record variables (e.g., rec_Account)inp_ Input variables (e.g., inp_RecordId)out_ Output variables (e.g., out_IsSuccess)Check_Account_Type)Action_[Verb]_[Object] (e.g., Action_Save_Contact)docs/flow-best-practices.md (in sf-flow) for comprehensive guidanceDML in Loop: Collect records in collection variable β Single DML after loop Missing Fault Path: Add fault connector from DML β error handling β log/display Self-Referencing Fault: Error "element cannot be connected to itself" β Route fault connector to DIFFERENT element Element Duplicated: Error "Element X is duplicated" β Group ALL elements of same type together Field Not Found: Verify field exists, deploy field first if missing Insufficient Permissions: Check profile permissions, consider System mode
| Error Pattern | Fix |
|---|---|
$Record__Prior in Create-only | Only valid for Update/CreateAndUpdate triggers |
| "Parent.Field doesn't exist" | Use TWO Get Records (child then parent) |
$Record__c loop fails | Use $Record directly (single context, not collection) |
XML Gotchas: See docs/xml-gotchas.md (in sf-flow)
| Scenario | Solution |
|---|---|
| >200 records | Warn limits, suggest scheduled flow |
| >5 branches | Use subflows |
| Cross-object | Check circular deps, test recursion |
| Production | Deploy Draft, activate explicitly |
| Unknown org | Use standard objects (Account, Contact, etc.) |
Debug: Flow not visible β deploy report + permissions | Tests fail β Debug Logs + bulk test | SandboxβProd fails β FLS + dependencies
See shared/docs/cross-skill-integration.md (project root)
Deployment: See Phase 4 above - sf-devops-architect is MANDATORY.
When sf-ai-agentforce requests a Flow:
flow://FlowName targetsVariable Name Matching: When creating Flows for Agentforce agents:
inp_AccountId, out_AccountName)| Direction | Pattern |
|---|---|
| sf-flow β sf-metadata | "Describe Invoice__c" (verify fields before flow) |
| sf-flow β sf-devops-architect | Deploy with validation - MANDATORY |
| sf-flow β sf-data | "Create 200 test Accounts" (test data after deploy) |
| sf-ai-agentforce β sf-flow | "Create Autolaunched Flow for agent action" - sf-flow is MANDATORY |
Dependencies (optional): sf-deploy, sf-metadata, sf-data | API: 62.0 | Mode: Strict (warnings block) | Python validators recommended
MIT License. See LICENSE file. Copyright (c) 2024-2025 Jag Valaiyapathy