I have spent more time debugging Scheduled Flow problems than I care to admit. The feature is genuinely powerful, but it has enough subtle behavior differences from other flow types that it catches even experienced admins off guard. Let me walk you through the patterns that work and the pitfalls that have tripped up real orgs in production.
Two Different Features That Sound the Same
The first thing to get right is the distinction between two Flow features that both involve scheduling:
Schedule-Triggered Flows
Schedule-Triggered Flows run on a time schedule you define — daily, weekly, or at a specific time — and operate on a batch of records that match a SOQL filter you specify. Think of it as a cron job with a SOQL query built in.
Use when: You need to process a set of records on a recurring basis regardless of when each record was created.
Scheduled Paths
Scheduled Paths are part of Record-Triggered Flows. When a record meets a trigger condition, instead of processing immediately, the flow sets a scheduled wait. After the wait period expires, the scheduled path executes.
Use when: You need per-record time-delayed automation (e.g., “3 days after this case was created”).
They serve different purposes and have different governor limit profiles. Conflating them leads to choosing the wrong tool.
Schedule-Triggered Flows
When to Use Them
Use a Schedule-Triggered Flow when:
- You need to process a set of records on a recurring basis regardless of when each record was created
- The triggering condition is time-based (e.g., every morning, check all open cases where last activity was more than 7 days ago)
- You do not need per-record timing — every record in the batch gets processed at the same scheduled time
Common use cases:
- Daily SLA breach checks
- Weekly data quality reports
- Nightly sync operations
- Monthly digest emails to account owners
Batch Size: The 2000 Record Limit
Schedule-Triggered Flows process records in batches of up to 2000 records per transaction. If your SOQL filter returns 5000 records, the flow runs across multiple batches automatically. Salesforce handles the batching — you do not need to implement it.
- Avoid operations that aggregate across all records in a single transaction (counts, rollups) — each batch only has visibility into its own 2000 records
- Actions that send emails count against per-transaction email limits (500 per transaction)
- Platform Event publishes follow the standard 150 DML statements per transaction limit
The SOQL Filter Matters
The object and filter you define at the start of a Schedule-Triggered Flow is executed as a SOQL query. You should treat this like writing a performant SOQL query:
- Filter on indexed fields where possible (Id, Name, ExternalId__c, CreatedDate, SystemModstamp)
- Add a status filter to exclude already-processed records (so the batch does not grow unbounded)
- Use date literals (
LAST_N_DAYS:7) rather than computed date values where possible
A Schedule-Triggered Flow that queries all Accounts with no filter will process every Account in your org every time it runs. Always include a status filter.
Scheduled Paths in Record-Triggered Flows
The Re-Evaluation Behavior
When a record enters a Scheduled Path wait, Salesforce does not store a snapshot of the record at that moment. When the scheduled time arrives, Salesforce re-queries the record and re-evaluates the entry conditions for the path.
Practical implication: If your Scheduled Path entry condition is “Status equals New” and the record’s Status changes to “In Progress” before the wait expires, the Scheduled Path will not execute when the timer fires. The record no longer meets the entry condition.
This is sometimes the desired behavior (don’t send a follow-up if the case was already resolved), but it surprises people who expect the path to always execute.
If you need to guarantee execution regardless of field changes, use a Schedule-Triggered Flow with a filter that does not reference the status field.
Time Zones: The UTC Reality
Scheduled Paths evaluate their wait times in UTC. When you set a wait of “1 day from CreatedDate”, Salesforce adds 24 hours to the UTC timestamp of CreatedDate. This means:
- A record created at 11 PM US Eastern (4 AM UTC next day) with a “1 day” wait executes 24 hours after 4 AM UTC — which is 4 AM UTC the following day, or 11 PM Eastern
- This may or may not align with your business expectations
Business Hours Workarounds
For business-hours-aware automation, you have two options:
- Use a larger wait window (e.g., 2 days) and accept that execution time varies slightly
- Combine with a Flow scheduled path that waits until a specific time of day using a dynamic date formula
// Formula for "next 9 AM after 3 days"
DATEVALUE(CreatedDate) + 3 + (9/24) // Rough approximationFor precise business-hours scheduling, custom Apex with System.scheduleBatch gives you more control than Flow.
Bypass Patterns for Scheduled Paths
Scheduled Paths respect the same bypass mechanisms as other Record-Triggered Flow paths. You can add an entry condition at the scheduled path level that references a checkbox field like Automation_Paused__c = false. Setting this field on the record before the wait expires prevents the path from executing.
Practical Use Case Patterns
Pattern 1: Follow-Up Email Sequence
A classic use case: send a follow-up email 3 days after a lead is created if it has not been contacted.
Flow design:
- Entry condition: Lead is created
- Immediate path: Set a “Follow-Up Scheduled” timestamp field
- Scheduled path (3 days): Check
Status != Contacted— if true, send email and increment follow-up counter - Second scheduled path (7 days): Check
Status != Contacted AND Follow_Up_Count__c >= 1— if true, create a Task for the owner
The re-evaluation behavior works in your favor here: if the rep contacts the lead before day 3, the path does not fire.
Pattern 2: SLA Breach Warning
Flow design:
- Entry condition: Case is created with Priority = High
- Scheduled path (4 hours before SLA): Check
Status != Closed— send Slack/email alert to case owner and manager - Scheduled path (at SLA deadline): Check
Status != Closed— escalate to supervisor, set Escalated flag
Note: For the “4 hours before SLA” timing, you would use a dynamic offset based on a custom SLA deadline date/time field populated by your business logic.
Pattern 3: Data Quality Monitoring with Schedule-Triggered Flow
Run daily at 8 AM to find Opportunities missing required fields 30 days before close:
Object: Opportunity
Filter: CloseDate = NEXT_N_DAYS:30
AND StageName != 'Closed Won'
AND StageName != 'Closed Lost'
AND (Product_Line__c = null OR Competitor__c = null)For each matching record, the flow:
- Creates a Task assigned to the opportunity owner
- Sends a Chatter post to the opportunity record
- Sets a
Data_Quality_Alert__ccheckbox (so the same record is not flagged again tomorrow — update your filter to exclude records with this set)
Common Pitfalls
Bulkification in Schedule-Triggered Flows
Schedule-Triggered Flows are inherently bulkified — the 2000-record batch is processed together. However, operations within the flow still need to follow bulkification principles:
- Get Records elements: Each Get Records element is one SOQL query regardless of batch size. Use these efficiently.
- Update Records elements: Accumulate records and update in a single Update Records element at the end, not one per record.
- Apex actions called from Flow: Your Apex invocable methods must accept
List<>parameters and process them in bulk. A Flow invocable method called inside a loop is called once per record — this is a governor limit disaster.
The Problem
You built a Scheduled Path on a Case record-triggered Flow: “If Status = New after 48 hours, escalate to manager.” Users are complaining that cases are being escalated even after they have been resolved. The Flow appears to ignore the re-evaluation check.
The Solution
The re-evaluation is working correctly — the issue is your entry condition. The Scheduled Path re-evaluates whether the record still meets the entry conditions when the timer fires. Check that your path entry condition explicitly filters for Status = New at the scheduled path level, not just at the Flow trigger entry. If the condition is only on the Flow trigger (not on the Scheduled Path itself), the path will execute for all records that originally triggered the Flow, regardless of their current status.
Always add a “processed” guard field (a checkbox like Daily_Alert_Sent__c) to your Schedule-Triggered Flow filter. Without it, every execution of the Flow re-processes the same matching records. Update the field at the end of the Flow and include Daily_Alert_Sent__c = false in your SOQL filter. Reset it with a separate nightly Flow if the record should be eligible again after 24 hours.
Monitoring Scheduled Flows
Salesforce provides the Time-Based Workflow or Paused and Waiting Interviews list to see pending scheduled flow executions. Navigate to Setup > Flows > Paused and Waiting Flow Interviews.
Querying Flow Errors Programmatically
You can also query scheduled interviews directly:
// NOTE: FlowExecutionErrorEvent is a Platform Event, not a standard
// queryable object. You cannot use SOQL to query it. Instead, consume
// it via an Apex trigger on the event, a CometD/Streaming subscription,
// or a Platform Event-Triggered Flow.
//
// Example: Apex trigger to capture flow errors
trigger FlowErrorHandler on FlowExecutionErrorEvent (after insert) {
for (FlowExecutionErrorEvent evt : Trigger.New) {
System.debug('Flow error: ' + evt.FlowApiName + ' — ' + evt.Message);
}
}For production monitoring, setting up a Flow Error Email (Setup > Process Automation Settings > Apex Exception Email) ensures you are notified when scheduled flows fail.
What is the most complex scheduling automation you have built in Flow, and did you run into any of these pitfalls along the way?
Knowledge Check
How did this article make you feel?
Comments
Salesforce Tip