SfSkills Domains Docs GitHub

Home / flow / flow-transaction-finalizer-patterns

flow-transaction-finalizer-patterns

Use when a Flow needs to do work that must survive the triggering transaction — post-commit notifications, callouts, audit rows, or compensating actions. Covers Flow Transaction Control element, scheduled paths, Platform Event + finalizer. NOT for general Flow async decisions (see async-selection) — use apex/apex-transaction-finalizers.

Open the skill folder on GitHub


Flow Transaction Finalizer Patterns

Purpose

Salesforce transactions are atomic: either every DML commits or none does. But some work should only run AFTER commit — sending an email, firing a webhook, writing an audit log to an external system, or issuing a compensating action if a callout later fails. Getting this wrong produces two common failures: "email sent but record rolled back," or "record saved but downstream never notified." This skill codifies the patterns Flow designers should use for reliable post-transaction behavior, and when to escalate to Apex Queueable with a Finalizer.

  1. Classify the step. Pre-commit (validation), in-transaction (DML), or

    post-commit (notify / callout / external effect)?

  2. Separate two guarantees that sound alike. Post-commit execution — the

    work runs only if the transaction committed — is available three ways in Flow. Post-outcome execution — a callback that runs whether the work succeeded or threw — is not available in Flow at all.

  3. For post-commit work, default to the Run Asynchronously path. It runs

    after the original transaction for the triggering record is successfully committed, in its own transaction, and it is what makes a callout legal. It is available only on after-save record-triggered flows, and you cannot give it a time.

  4. Use Platform Events when the consumers should be decoupled, not merely

    deferred — three independent subscribers where adding a fourth should not touch the publisher. Confirm the definition is set to Publish After Commit.

  5. **Cross into Apex when the work must report its own outcome or retry with a

    bound.** A Queueable's System.Finalizer reads ParentJobResult.SUCCESS or UNHANDLED_EXCEPTION and can re-enqueue a failed job up to five times.

  6. Design idempotency at the boundary. Every mechanism here can run twice.

    The key must be deterministic and derived from the record — never $Flow.InterviewGuid, which differs on exactly the run you are guarding against.

  7. Log every run and every give-up. Nobody is watching post-commit work. A

    silent stop at the platform's retry ceiling is indistinguishable from success.

Available Mechanisms

MechanismRunsSurvives rollbackCalloutsReports its own outcome
Pre-commit Flow stepSame txnNoNoNo
After-save flow, immediate pathSame txn, before commitNoNoNo
Run Asynchronously pathNew txn, after commitYesYesNo
Platform Event (Publish After Commit) → subscriberNew txnYesYes, in the subscriberNo
Queueable + System.FinalizerAsync, after commitYesYesYes

The last column is the whole decision. Everything above the bottom row defers work; only the bottom row tells you what happened to it.

Patterns

Pattern A: Run Asynchronously Path

The default for "must not fire on rollback" and for any callout. Open the after-save record-triggered flow → Start element → include a Run Asynchronously path. The path runs after the original transaction for the triggering record commits, so the external system is never told about a record that rolled back. Add a fault connector to a log record — it is the only evidence the path ran.

Subject to the asynchronous per-transaction Apex limits (200 SOQL, 60,000 ms CPU, 12 MB heap): higher than synchronous, not absent. Queued entries are visible on the Time-Based Workflow page in Setup.

Pattern B: Platform Event For Fan-Out

Choose this over three asynchronous paths when the consumers are genuinely independent — the benefit is that adding a fourth subscriber does not touch the publisher, not extra durability. Verify Publish Behavior on the event definition: Publish Immediately fires even on rollback, and it draws on a different governor allocation. See flow/flow-and-platform-events.

Pattern C: Apex Queueable + Finalizer

Reach for this when the requirement is to know whether the work succeeded and act on the answer. Flow calls an Invocable Action that enqueues a Queueable; the Queueable attaches a finalizer that writes the outcome and, on failure, decides whether to retry.

The ceilings that shape the design: one finalizer per Queueable job; a failed job can be re-enqueued five times and the count resets on success; the finalizer may enqueue a single asynchronous job; callouts are allowed. Re-enqueue replays the whole Queueable body, so everything before the failure point runs again.

templates/apex/ has no canonical QueueableWithFinalizer today — the illustrative shape is in references/examples.md, and this is a flagged template gap rather than an oversight.

Durability Cheatsheet

Anti-Patterns (see references/llm-anti-patterns.md)

Official Sources Used

The full annotated list is in references/well-architected.md.

Package files