Introduction: The Use Case
Onboarding is one of the most orchestrated workflows in many organizations. Each new deal can kick off dozens of tasks across multiple teams such as implementation, training, security, billing, legal - often with strict ordering (some steps can’t start until others finish). Our client needed that orchestration to happen automatically inside Salesforce when an Opportunity entered a specific stage, with tasks assigned to the right users and email notifications sent immediately. Two non-negotiables:
- Dependencies between tasks must be preserved.
- No duplicates if someone toggles the Opportunity stage.
The standard Task object didn’t give us the flexibility we needed for template-driven creation and dependency wiring, so we introduced custom objects and a flow pattern that’s reliable, scalable, and idempotent.
Requirements & Challenges
Functional requirements
- Template-driven tasks: Define the onboarding steps once; reuse them for every qualifying Opportunity.
- Dependencies: Certain tasks must wait on others (e.g., “Kickoff Call” before “Provision Accounts”).
- Automated assignment: Tasks route to users/roles from the template.
- Onboarding Updates: The onboarding process may change (added or removed tasks), so the process needs to allow for changes to flow through to future onboardings
- Notifications: Assignees get an email when tasks are created.
Operational safeguards
- Idempotency: If the stage toggles or the Flow re-fires, don’t create duplicates.
- Bulk-safe: Create and update in collections to stay within Flow limits.
- Resilience: If something faults, the automation can safely try again later.
Flow-specific constraint
- Salesforce Flow does not provide map/dictionary types. That means we can’t store “TemplateId → TaskId” in a native map. We handled dependency wiring with a bridge field + loops pattern instead (described below).
Data Model (focused fields)
Onboarding Task Template
- Name, Description__c
- Assigned_User__c or Assigned_Role__c
- Prerequisite__c (Lookup → Onboarding Task Template) (the template this one depends on)
- Active__c (Checkbox)
Onboarding Task
- Name, Description__c, Status__c
- Related_Opportunity__c (Lookup → Opportunity)
- Assigned_User__c (Lookup → User)
- Dependency__c (Lookup → Onboarding Task) (the actual, created task this one depends on)
- Source_Template__c (Lookup → Onboarding Task Template)
- Onboarding_Template_Dependent_Task__c (bridge field: stores the Template Id of the prerequisite so we can resolve it later)
Opportunity
- Onboarding_Initiated__c (Checkbox) — idempotency guard
Flow Strategy (Record-Triggered on Opportunity)
0) Entry conditions & idempotency guard
- Trigger when: Stage transitions to your onboarding stage AND Onboarding_Initiated__c = FALSE.
- If Onboarding_Initiated__c = TRUE → exit immediately (prevents re-creation on stage toggles).
1) Fetch active templates
- Get all Onboarding Task Template records where Active__c = TRUE.
- If none → optional log/Chatter → exit.
2) Loop A — Create tasks (and carry prerequisites forward)
- Initialize a collection variable createdTasks (empty).
- For each template:
- Optional de-dup safety: Query for an existing Onboarding Task where
Related_Opportunity__c = {!$Record.Id} AND Source_Template__c = Template.Id.
If found → skip (protects against edge cases before the flag is set).
- Create Onboarding Task with:
- Core fields from template (Name/Description/Assigned).
- Related_Opportunity__c = {!$Record.Id}
- Source_Template__c = Template.Id
- Onboarding_Template_Dependent_Task__c = Template.Prerequisite__c (store the template prerequisite Id for later)
- Add the new task to createdTasks.
3) Loop B — Resolve dependencies (no map needed)
- For each task in createdTasks:
- If Onboarding_Template_Dependent_Task__c is populated:
- Inner loop: iterate again over createdTasks to find the predecessor task where
predecessor.Source_Template__c == currentTask.Onboarding_Template_Dependent_Task__c.
- When found: set currentTask.Dependency__c = predecessor.Id. (You now have task-to-task linkage.)
- After the loop, bulk update createdTasks to persist Dependency__c.
Why a nested loop? Flow lacks map/dictionary types. The bridge field (Onboarding_Template_Dependent_Task__c) carries the template prerequisite forward so we can match it to the actual task created from that template by scanning the in-memory createdTasks collection.
4) Notify assignees
- Send email (Email Alert or Send Email (Enhanced)) to each Assigned_User__c in createdTasks.
5) Mark onboarding initiated
- Update Opportunity: Onboarding_Initiated__c = TRUE.
This is your primary idempotency guard for any future triggers.
6) Fault handling (recommended)
- If any step faults before setting the flag, the record remains safe to re-process on the next qualifying change.
- Consider logging to a simple “Onboarding Log” object for visibility.
Why this pattern works
- Template-driven & consistent: Every onboarding follows the same blueprint.
- Dependency-correct: The bridge field + nested loop resolves “template prerequisite” → “actual predecessor task” without needing maps.
- Idempotent by design: The Onboarding_Initiated__c flag prevents reruns from duplicating tasks, and the optional de-dup query adds belt-and-suspenders protection.
- Governance-friendly: Collection creates/updates keep DML usage low and predictable.
- Adoption-oriented: Email notifications make responsibilities clear from day one.
Conclusion
By combining custom objects, a carefully designed Flow pattern, and an idempotency safeguard, we transformed a complex, error-prone onboarding process into a consistent, automated workflow inside Salesforce. The solution ensures that every Opportunity entering onboarding follows the same structure, with dependencies intact, notifications sent, and no risk of duplicate tasks.
This approach highlights a broader lesson: when standard Salesforce features don’t quite fit, extending with custom objects and creative Flow design can unlock powerful new capabilities.
If your organization is facing similar challenges—whether it’s onboarding, project task management, or other process automation—our team at Zaghop specializes in designing and implementing scalable Salesforce solutions. We’d be happy to discuss how these patterns could be adapted to your use case.