Flow Transactional Boundaries
Core concept — what is a "transaction" in Flow?
A Salesforce transaction is a unit of work bounded by:
- Start: a DML statement from a trigger, a user save from the UI, an Apex entry point (controller action, @HttpPost, future start), or a scheduler firing.
- End: the DML commits, or a rollback, or a governor-limit exception terminates the transaction.
Inside one transaction:
- SOQL queries count against the 100-query limit.
- DML rows count against the 10,000-row limit.
- CPU time counts against the 10-second limit (or 60s for async).
- All triggers, Before-Save flows, After-Save flows, validation rules, escalations, and Apex sharing share these limits.
Flow can either join an existing transaction or start its own. The choice changes everything about how the flow behaves under load and what happens on failure.
Transaction behavior per Flow entry point
Before-Save record-triggered flow
- Joins the triggering DML transaction.
- No DML elements allowed. Can only update the triggering record (via assignments), look up related records, and loop over collections.
- No callouts. Not supported in this context.
- No subflows that do DML. Autolaunched subflows that would do DML are blocked.
- Runs BEFORE the record is written, so changes to field values are free (no extra DML).
- Governor limits are shared with the rest of the transaction — but Before-Save is cheap, so the shared cost is usually negligible.
Use when: deriving field values on the same record. Setting Status__c from Amount + Stage. Copying account fields onto a case. Calculating a normalized phone.
After-Save record-triggered flow
- Joins the triggering DML transaction.
- All elements allowed: DML, callouts (must be wrapped in a
Transaction Control boundary or routed via a subflow to a pausable context), loops, subflows.
- DML row count adds to the shared 10,000-row limit with the trigger and any other after-save automations.
- Callouts from After-Save inline will throw
System.CalloutException: Callout from triggers are currently not supported. To do callouts, either (a) mark the flow "Run Asynchronously" (scheduled-path-of-zero-minutes), or (b) emit a Platform Event and subscribe async.
Use when: creating related records, posting to Chatter, sending emails synchronously, updating unrelated records in the same transaction.
Scheduled Path (on an after-save record-triggered flow)
- Starts a new transaction.
- Fresh governor limits. No DML sharing with the original trigger.
- The path fires at a configurable offset (+0 minutes = async-now, +30 days = SLA reminder).
- Callouts allowed.
- Replays independently of the original save. If the original save rolled back, the scheduled path never fires.
- If the record is deleted before the path fires, the path silently drops.
Use when: you want "eventually consistent" work that must not block the save; SLA reminders; fanout to related records; callouts.
Autolaunched flow called from Apex
- Joins the calling Apex transaction.
- Governor limits shared with Apex.
- Apex can call a flow via
Flow.Interview.createInterview(flowName, params).start() or via @InvocableMethod.
- Exceptions in the flow bubble up as
Flow.FlowException; wrap the Apex call in try/catch if the caller wants to continue on flow failure.
Use when: Apex orchestrates the transaction but needs a declarative subroutine (admin-maintainable logic).
Autolaunched flow called as a subflow
- Joins the parent flow's transaction.
- Shares limits with parent.
- A subflow cannot escape the parent's transactional context — if the parent is Before-Save, the subflow is too.
Screen flow (user-facing)
- Each save point in a screen flow (a DML element then a subsequent screen) is its own transaction.
- The commit happens when the user clicks Next past a screen following a DML element, or at the final Finish.
- If the user navigates away, uncommitted changes are lost.
- Pause elements persist the interview to the database and end the current transaction.
Screen flow with a Pause element
- Pause writes the
FlowInterview record, ends the current transaction, and schedules resume.
- On resume (user click, time trigger, Platform Event received), a new transaction begins.
- All state (variables, collections) is serialized and deserialized — large state can hit serialization limits.
Scheduled flow (fired by the clock, not by a record)
- Starts a new transaction.
- Fresh governor limits.
- Processes the record set defined in the flow's scheduler config; if the set is > 250k rows, the flow silently stalls on limits — escalate to Batchable Apex.
Platform-Event-triggered flow
- Fires on event delivery; each event delivery is a new transaction.
- High-Volume Platform Events are delivered in batches (up to 2,000 per subscriber execution).
- Fresh governor limits per batch.
- Ordering not guaranteed unless you use "Published After Commit" on the publisher side AND subscribe with a single subscriber.
Orchestration stage
- Each stage transition is a new transaction.
- Stage state persists in the
OrchestrationInstance record.
- Work items (human tasks) assigned at stage entry; stage advances only when all work items complete.
- Ideal for multi-day workflows that would overrun screen-flow session timeouts.
Recommended Workflow
- Read
standards/decision-trees/flow-pattern-selector.md to confirm the flow subtype.
- Classify the work as (a) field derivation on same record, (b) inline DML across records, (c) needs callouts, (d) must be delayed / scheduled, (e) spans multiple user sessions, (f) spans multiple humans.
- Pick the transaction boundary per the table in this skill.
- Draw the boundary diagram — list every DML, every callout, every subflow and mark which transaction it runs in.
- Compute the governor-limit budget per boundary — if a Before-Save joins a transaction already running 85 SOQL, your flow's 15 SOQL budget is tight.
- Plan idempotency — any work in a new transaction must be safe to run twice (scheduled paths, platform-event fanouts retry on failure).
- Document the boundary diagram in the flow description so downstream maintainers don't bypass the reasoning.
Key patterns
Pattern 1 — "Derive then act" split
Before-Save derives fields on the same record; After-Save (same flow or a sibling flow) creates related records.
[Trigger record updated]
│
▼
[Before-Save Flow] ← derives Normalized_Phone__c, Region__c
│ (same txn)
▼
[Record written to DB]
│ (same txn)
▼
[After-Save Flow] ← creates related Task + Chatter post
│ (same txn)
▼
[Transaction commits]
Savings: the Before-Save avoids a second DML for the field update — roughly a 90% cost reduction vs an After-Save that re-updates.
Pattern 2 — "Callout-required → Scheduled Path +0"
An After-Save flow needs to call an external service. Inline callouts are blocked in a trigger context. Route via a Scheduled Path with +0 minutes.
[Record inserted]
│
▼
[After-Save Flow entry]
│
▼
[Scheduled Path: +0 minutes, criteria: Status = 'Ready']
│ (new txn, fresh limits)
▼
[HTTP callout to vendor]
│
▼
[Update record with vendor ref]
Key detail: the +0 minutes doesn't mean "immediate" — the scheduler picks up the work, typically within 1–5 minutes. Not suitable for latency-sensitive needs.
Pattern 3 — Multi-day approval via Orchestration
Instead of a screen flow with pause elements (fragile, limited to one user's session), use an Orchestration with three stages: Legal Review → Procurement Review → Customer Sign-off. Each stage is a new transaction; each assigns a work item to a named user or queue.
See skills/flow/orchestration-flows for stage authoring.
Pattern 4 — "Platform Event fanout" for decoupled writes
A single trigger needs to update 5 unrelated objects. Instead of inline After-Save DML (which shares the 10,000-row limit with the trigger), publish one Platform Event and have 5 independent PE-triggered flows each handle one target. Each subscriber runs in its own transaction with fresh limits.
Bulk safety
- Before-Save flows are the most bulk-safe — no DML, simple element set, shared-limit impact is tiny.
- After-Save flows must be written with bulk DML in mind — use a Create Records element with a collection, never a loop with a DML inside.
- Scheduled Paths process records in batches of up to 200. If your record set per trigger event is larger than that, multiple scheduled-path executions run in parallel — plan for concurrent writes and set
Allow Concurrent Execution = true only when truly safe.
- Platform-Event-triggered flows receive events in batches of up to 2,000 event messages. 2,000 is both the maximum and the default, and it is the same for standard-volume and high-volume events;
PlatformEventSubscriberConfig can only lower it (valid range 1–2,000), never raise it. The flow's loop body runs once per event in the batch; loops must be bulk-safe.
- Scheduled flows run once per scheduled execution and process the query result set. If you use a Loop element, every DML inside is one-per-record — convert to a collection and do a single Update Records.
Error handling
- Before-Save: errors bubble as record save errors (user sees the Flow Error message as a save error). No fault path needed; the triggering DML is rolled back.
- After-Save inline: errors roll back the entire triggering transaction unless wrapped in a fault path that catches + logs. Always wire a Fault connector (see
skills/flow/fault-handling).
- Scheduled Path: errors end the scheduled-path transaction; the original save is already committed, so no rollback. Salesforce retries the scheduled-path execution up to 3 times with exponential backoff, then marks it failed. Use the Flow runtime error report.
- Platform-Event-triggered: on failure, the event is re-queued (Standard PE) or dropped (High-Volume PE with non-idempotent subscriber). Write subscribers to be idempotent — use an external-id field to dedupe.
- Screen flow pause: if resume fails, the interview stays in "Paused Error" state. Monitor via the Paused and Waiting Interviews list.
Well-Architected mapping
- Reliability — transaction boundary choice determines rollback scope. Before-Save failures roll back the user's save (often desirable for validation-like work). Scheduled Paths isolate failures (won't poison the original save). Pick based on whether the work is essential (inline) or eventually consistent (async).
- Performance — Before-Save is much cheaper than After-Save for field derivation. Scheduled Paths trade latency for limit isolation. Orchestrations add persistence overhead — use only when multi-day work genuinely needs it.
- Security — cross-transaction work (Scheduled Paths, Platform Events) runs as the "Automated Process" user or the record owner depending on version settings. CRUD/FLS posture may differ from the original trigger; verify with
skills/apex/apex-security-crud-fls principles.
Gotchas
See references/gotchas.md.
Testing
See skills/flow/flow-testing. Key testing concerns per boundary:
- Before-Save: assert field values on the returned record without DML.
- After-Save: assert DML results via SOQL after test setup commits.
- Scheduled Path: invoke
Test.startTest() / Test.stopTest() — scheduled-path records fire synchronously inside the test block.
- Platform-Event-triggered: use
Test.startTest() / Test.stopTest() to flush the event bus.
Official Sources Used
1---2name: flow-transactional-boundaries3description: Reason about when a Flow is inside the caller's transaction vs starts its own. Pick Before-Save vs After-Save vs Async Path vs Pause + Resume when transaction boundaries matter. Covers governor-limit sharing, DML sequencing, recoverability, and the limits that reset when a new transaction starts. NOT for choosing Flow vs Apex (use admin/process-automation-selection) or record-triggered design — use flow/record-triggered-flow-patterns.4---56# Flow Transactional Boundaries78## Core concept — what is a "transaction" in Flow?910A Salesforce transaction is a unit of work bounded by:1112- **Start:** a DML statement from a trigger, a user save from the UI, an Apex entry point (controller action, @HttpPost, future start), or a scheduler firing.13- **End:** the DML commits, or a rollback, or a governor-limit exception terminates the transaction.1415Inside one transaction:16- SOQL queries count against the 100-query limit.17- DML rows count against the 10,000-row limit.18- CPU time counts against the 10-second limit (or 60s for async).19- All triggers, Before-Save flows, After-Save flows, validation rules, escalations, and Apex sharing share these limits.2021Flow can either **join** an existing transaction or **start its own**. The choice changes everything about how the flow behaves under load and what happens on failure.2223## Transaction behavior per Flow entry point2425### Before-Save record-triggered flow2627- Joins the triggering DML transaction.28- **No DML elements allowed.** Can only update the triggering record (via assignments), look up related records, and loop over collections.29- **No callouts.** Not supported in this context.30- **No subflows that do DML.** Autolaunched subflows that would do DML are blocked.31- Runs BEFORE the record is written, so changes to field values are free (no extra DML).32- Governor limits are **shared** with the rest of the transaction — but Before-Save is cheap, so the shared cost is usually negligible.3334Use when: deriving field values on the same record. Setting `Status__c` from `Amount` + `Stage`. Copying account fields onto a case. Calculating a normalized phone.3536### After-Save record-triggered flow3738- Joins the triggering DML transaction.39- All elements allowed: DML, callouts (must be wrapped in a `Transaction Control` boundary or routed via a subflow to a pausable context), loops, subflows.40- DML row count **adds to** the shared 10,000-row limit with the trigger and any other after-save automations.41- **Callouts from After-Save inline will throw `System.CalloutException: Callout from triggers are currently not supported`.** To do callouts, either (a) mark the flow "Run Asynchronously" (scheduled-path-of-zero-minutes), or (b) emit a Platform Event and subscribe async.4243Use when: creating related records, posting to Chatter, sending emails synchronously, updating unrelated records in the same transaction.4445### Scheduled Path (on an after-save record-triggered flow)4647- Starts a **new transaction**.48- Fresh governor limits. No DML sharing with the original trigger.49- The path fires at a configurable offset (+0 minutes = async-now, +30 days = SLA reminder).50- Callouts allowed.51- **Replays independently of the original save.** If the original save rolled back, the scheduled path never fires.52- If the record is deleted before the path fires, the path silently drops.5354Use when: you want "eventually consistent" work that must not block the save; SLA reminders; fanout to related records; callouts.5556### Autolaunched flow called from Apex5758- Joins the calling Apex transaction.59- Governor limits shared with Apex.60- Apex can call a flow via `Flow.Interview.createInterview(flowName, params).start()` or via `@InvocableMethod`.61- Exceptions in the flow bubble up as `Flow.FlowException`; wrap the Apex call in try/catch if the caller wants to continue on flow failure.6263Use when: Apex orchestrates the transaction but needs a declarative subroutine (admin-maintainable logic).6465### Autolaunched flow called as a subflow6667- Joins the parent flow's transaction.68- Shares limits with parent.69- **A subflow cannot escape the parent's transactional context** — if the parent is Before-Save, the subflow is too.7071### Screen flow (user-facing)7273- Each **save point** in a screen flow (a DML element then a subsequent screen) is its own transaction.74- The commit happens when the user clicks Next past a screen following a DML element, or at the final Finish.75- If the user navigates away, uncommitted changes are lost.76- Pause elements **persist the interview to the database** and end the current transaction.7778### Screen flow with a Pause element7980- Pause writes the `FlowInterview` record, ends the current transaction, and schedules resume.81- On resume (user click, time trigger, Platform Event received), a **new transaction** begins.82- All state (variables, collections) is serialized and deserialized — large state can hit serialization limits.8384### Scheduled flow (fired by the clock, not by a record)8586- Starts a new transaction.87- Fresh governor limits.88- Processes the record set defined in the flow's scheduler config; if the set is > 250k rows, the flow silently stalls on limits — escalate to Batchable Apex.8990### Platform-Event-triggered flow9192- Fires on event delivery; each event delivery is a **new transaction**.93- High-Volume Platform Events are delivered in batches (up to 2,000 per subscriber execution).94- Fresh governor limits per batch.95- **Ordering not guaranteed** unless you use "Published After Commit" on the publisher side AND subscribe with a single subscriber.9697### Orchestration stage9899- Each stage transition is a new transaction.100- Stage state persists in the `OrchestrationInstance` record.101- Work items (human tasks) assigned at stage entry; stage advances only when all work items complete.102- Ideal for multi-day workflows that would overrun screen-flow session timeouts.103104## Recommended Workflow1051061. **Read `standards/decision-trees/flow-pattern-selector.md`** to confirm the flow subtype.1072. **Classify the work** as (a) field derivation on same record, (b) inline DML across records, (c) needs callouts, (d) must be delayed / scheduled, (e) spans multiple user sessions, (f) spans multiple humans.1083. **Pick the transaction boundary** per the table in this skill.1094. **Draw the boundary diagram** — list every DML, every callout, every subflow and mark which transaction it runs in.1105. **Compute the governor-limit budget per boundary** — if a Before-Save joins a transaction already running 85 SOQL, your flow's 15 SOQL budget is tight.1116. **Plan idempotency** — any work in a new transaction must be safe to run twice (scheduled paths, platform-event fanouts retry on failure).1127. **Document the boundary diagram in the flow description** so downstream maintainers don't bypass the reasoning.113114## Key patterns115116### Pattern 1 — "Derive then act" split117118Before-Save derives fields on the same record; After-Save (same flow or a sibling flow) creates related records.119120```121[Trigger record updated]122 │123 ▼124[Before-Save Flow] ← derives Normalized_Phone__c, Region__c125 │ (same txn)126 ▼127[Record written to DB]128 │ (same txn)129 ▼130[After-Save Flow] ← creates related Task + Chatter post131 │ (same txn)132 ▼133[Transaction commits]134```135136Savings: the Before-Save avoids a second DML for the field update — roughly a 90% cost reduction vs an After-Save that re-updates.137138### Pattern 2 — "Callout-required → Scheduled Path +0"139140An After-Save flow needs to call an external service. Inline callouts are blocked in a trigger context. Route via a Scheduled Path with +0 minutes.141142```143[Record inserted]144 │145 ▼146[After-Save Flow entry]147 │148 ▼149[Scheduled Path: +0 minutes, criteria: Status = 'Ready']150 │ (new txn, fresh limits)151 ▼152[HTTP callout to vendor]153 │154 ▼155[Update record with vendor ref]156```157158Key detail: the +0 minutes doesn't mean "immediate" — the scheduler picks up the work, typically within 1–5 minutes. Not suitable for latency-sensitive needs.159160### Pattern 3 — Multi-day approval via Orchestration161162Instead of a screen flow with pause elements (fragile, limited to one user's session), use an Orchestration with three stages: Legal Review → Procurement Review → Customer Sign-off. Each stage is a new transaction; each assigns a work item to a named user or queue.163164See `skills/flow/orchestration-flows` for stage authoring.165166### Pattern 4 — "Platform Event fanout" for decoupled writes167168A single trigger needs to update 5 unrelated objects. Instead of inline After-Save DML (which shares the 10,000-row limit with the trigger), publish one Platform Event and have 5 independent PE-triggered flows each handle one target. Each subscriber runs in its own transaction with fresh limits.169170## Bulk safety171172- **Before-Save flows are the most bulk-safe** — no DML, simple element set, shared-limit impact is tiny.173- **After-Save flows must be written with bulk DML in mind** — use a Create Records element with a collection, never a loop with a DML inside.174- **Scheduled Paths process records in batches of up to 200.** If your record set per trigger event is larger than that, multiple scheduled-path executions run in parallel — plan for concurrent writes and set `Allow Concurrent Execution = true` only when truly safe.175- **Platform-Event-triggered flows** receive events in batches of up to **2,000** event messages. 2,000 is both the maximum and the default, and it is the same for standard-volume and high-volume events; `PlatformEventSubscriberConfig` can only lower it (valid range 1–2,000), never raise it. The flow's loop body runs once per event in the batch; loops must be bulk-safe.176- **Scheduled flows run once per scheduled execution** and process the query result set. If you use a Loop element, every DML inside is one-per-record — convert to a collection and do a single Update Records.177178## Error handling179180- **Before-Save:** errors bubble as record save errors (user sees the Flow Error message as a save error). No fault path needed; the triggering DML is rolled back.181- **After-Save inline:** errors roll back the entire triggering transaction unless wrapped in a fault path that catches + logs. Always wire a Fault connector (see `skills/flow/fault-handling`).182- **Scheduled Path:** errors end the scheduled-path transaction; the original save is already committed, so no rollback. Salesforce retries the scheduled-path execution up to 3 times with exponential backoff, then marks it failed. Use the Flow runtime error report.183- **Platform-Event-triggered:** on failure, the event is re-queued (Standard PE) or dropped (High-Volume PE with non-idempotent subscriber). Write subscribers to be idempotent — use an external-id field to dedupe.184- **Screen flow pause:** if resume fails, the interview stays in "Paused Error" state. Monitor via the Paused and Waiting Interviews list.185186## Well-Architected mapping187188- **Reliability** — transaction boundary choice determines rollback scope. Before-Save failures roll back the user's save (often desirable for validation-like work). Scheduled Paths isolate failures (won't poison the original save). Pick based on whether the work is essential (inline) or eventually consistent (async).189- **Performance** — Before-Save is much cheaper than After-Save for field derivation. Scheduled Paths trade latency for limit isolation. Orchestrations add persistence overhead — use only when multi-day work genuinely needs it.190- **Security** — cross-transaction work (Scheduled Paths, Platform Events) runs as the "Automated Process" user or the record owner depending on version settings. CRUD/FLS posture may differ from the original trigger; verify with `skills/apex/apex-security-crud-fls` principles.191192## Gotchas193194See `references/gotchas.md`.195196## Testing197198See `skills/flow/flow-testing`. Key testing concerns per boundary:199200- Before-Save: assert field values on the returned record without DML.201- After-Save: assert DML results via SOQL after test setup commits.202- Scheduled Path: invoke `Test.startTest()` / `Test.stopTest()` — scheduled-path records fire synchronously inside the test block.203- Platform-Event-triggered: use `Test.startTest() / Test.stopTest()` to flush the event bus.204205## Official Sources Used206207- Salesforce Help — Flow Run-Time Behavior: https://help.salesforce.com/s/articleView?id=sf.flow_concepts_runtime.htm208- Salesforce Developer — Trigger Order of Execution: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_order_of_execution.htm209- Salesforce Help — Scheduled Paths in Record-Triggered Flows: https://help.salesforce.com/s/articleView?id=sf.flow_concepts_trigger_scheduled_path.htm210- Salesforce Developer — Platform Events Developer Guide: https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/211- Salesforce Architects — Well-Architected Framework: https://architect.salesforce.com/design/architecture-framework/well-architected