SfSkills Domains Docs GitHub

Home / flow / auto-launched-flow-patterns

auto-launched-flow-patterns

Use when invoking auto-launched Flows from Apex (Flow.Interview), from external systems via REST API, from Platform Events, or from other Flows (subflow element). Covers input/output variable mapping, bulkification patterns, and error/fault handling. NOT for record-triggered flows — use flow/record-triggered-flow-patterns. NOT for schedule-triggered flows — use flow/scheduled-flows.

Open the skill folder on GitHub


Auto-Launched Flow Patterns

This skill activates when a practitioner needs to invoke an auto-launched (no-UI) Flow from Apex code, an external system via the REST API, a Platform Event, or a parent Flow — and must correctly map variables, respect governor limits, and handle faults.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

Concept 1: Flow.Interview — Apex-to-Flow Bridge

The Flow.Interview class is the Apex API for invoking auto-launched Flows. The standard pattern is:

  1. Build a Map<String, Object> of input variable name → value pairs.
  2. Call Flow.Interview.createInterview('Flow_API_Name', inputMap) to create an interview instance.
  3. Call interview.start() to execute the Flow.
  4. Call interview.getVariableValue('outputVarName') to read output variables after start() returns.

createInterview and start() are both synchronous. The Flow runs to completion (or fault) before start() returns. Any unhandled fault in the Flow causes start() to throw a System.FlowException, which must be caught in the Apex caller.

Type mapping: Apex types map to Flow types as follows — String → Text, Boolean → Boolean, Integer/Long/Decimal/Double → Number or Currency, Date → Date, DateTime → DateTime, Id/SObject → Record (use the SObject Id string when mapping). Collection variables map to List<> in Apex. If the types do not match, start() or getVariableValue() throws a runtime exception.

Concept 2: REST API Invocation via the Flows Resource

External systems (integration middleware, Experience Cloud pages, third-party apps) can start an auto-launched Flow using the Salesforce REST API:

Authentication requires a valid OAuth access token with the api and flow scopes. The running user must have the "Run Flows" permission. If the Flow performs DML or SOQL, the user must also have the relevant object-level permissions.

Concept 3: Platform Event as a Flow Trigger

An auto-launched Flow can be configured with a Start element that listens for a Platform Event. When the Platform Event fires, Salesforce starts one Flow interview per event message. This decouples the publisher (e.g., an Apex trigger) from the processing logic (the Flow). Key characteristics:

Concept 4: Calling an Auto-Launched Flow as a Subflow

A parent Flow can invoke another auto-launched Flow using the Subflow element. The parent maps its own variables to the child Flow's input variables and can receive output variables back. This enables modular, reusable logic packages. When the parent calls the subflow, the child Flow runs synchronously in the same transaction, sharing the same governor limit budget.


Common Patterns

Pattern 1: Apex Caller with Input/Output Variable Mapping

When to use: Business logic is managed in a Flow so admins can change it without a code deployment. Apex triggers the logic (e.g., during record save) and must read a result back.

How it works:

// In a trigger handler or service class
public static void applyPricingDiscount(List<Opportunity> opps) {
    for (Opportunity opp : opps) {
        Map<String, Object> inputs = new Map<String, Object>{
            'inputOpportunityId'  => opp.Id,
            'inputProductFamily'  => opp.Product_Family__c
        };

        Flow.Interview pricingFlow = Flow.Interview.createInterview(
            'Calculate_Opportunity_Discount',  // exact API name — case-sensitive
            inputs
        );

        try {
            pricingFlow.start();
        } catch (System.FlowException e) {
            // Unhandled Flow fault propagates as FlowException
            throw new AuraHandledException('Pricing flow failed: ' + e.getMessage());
        }

        Decimal discount = (Decimal) pricingFlow.getVariableValue('outputDiscountPercent');
        opp.Discount__c = discount;
    }
}

Critical detail — calling in a loop: The pattern above calls start() once per record inside a for-loop. This is acceptable only for small record sets. In a bulk context (200 records from a trigger), each start() call incurs separate SOQL and DML from inside the Flow. If the Flow issues one SOQL query, 200 records × 1 query = 200 SOQL calls against the 100-query limit. Bulk pattern: See Pattern 2.

Why not plain Apex: Pricing rules change frequently. By encoding them in Flow, an admin can update the logic without a code change or deployment window.

Pattern 2: Bulkification — Collect Records, Invoke Once with a Collection Variable

When to use: The Flow logic must be applied to all records in a bulk trigger invocation without hitting the SOQL or DML governor limits.

How it works:

  1. In the Flow, change the input variable type to Collection of Record (e.g., a collection of Opportunity records or a collection of SObject Ids as Text collection).
  2. Inside the Flow, process the collection using a Loop element rather than relying on the Apex caller to loop.
  3. In Apex, collect all records first, then call start() once passing the whole collection.
// Collect all records first
List<Opportunity> oppsToProcess = new List<Opportunity>(opps);

Map<String, Object> inputs = new Map<String, Object>{
    'inputOpportunities' => oppsToProcess
};

Flow.Interview bulkFlow = Flow.Interview.createInterview(
    'Bulk_Opportunity_Processor',
    inputs
);

try {
    bulkFlow.start();
} catch (System.FlowException e) {
    throw new AuraHandledException('Bulk flow failed: ' + e.getMessage());
}

List<SObject> results =
    (List<SObject>) bulkFlow.getVariableValue('outputProcessedRecords');

Why it works: A single start() call incurs the Flow's SOQL and DML once, regardless of collection size. The Flow's Loop element iterates internally without multiplying external API calls.

Pattern 3: REST API Invocation from External System

When to use: An external system (middleware, portal, third-party app) needs to trigger Flow logic in Salesforce without direct Apex access.

How it works:

POST /services/data/v62.0/actions/custom/flow/My_Auto_Launched_Flow
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "inputs": [
    {
      "inputAccountId": "001xx000003Gyb2",
      "inputRequestType": "Renewal"
    }
  ]
}

Response:

[
  {
    "actionName": "My_Auto_Launched_Flow",
    "errors": null,
    "isSuccess": true,
    "outputValues": {
      "outputCaseId": "500xx000001CXeU",
      "outputStatus": "Created"
    }
  }
]

The errors array is populated if the Flow throws a fault or if input variables do not match. Always check isSuccess before reading outputValues.

Why not Apex REST custom endpoint: Using the standard Flows REST resource avoids a custom Apex class, reducing the code surface area to maintain.


Decision Guidance

SituationRecommended ApproachReason
Apex needs to invoke Flow logic for a single record synchronouslyFlow.Interview.createInterview() + start() in ApexDirect, synchronous, output variables readable immediately
Apex trigger fires on up to 200 records and Flow does SOQL insideCollect all records, pass as collection variable, single start() callPrevents per-record SOQL multiplication against governor limits
External system needs to trigger a Flow via HTTPREST Flows resource (/actions/custom/flow/<ApiName>)No custom Apex endpoint needed; standard auth applies
Publisher needs to fire-and-forget async processingPlatform Event + Flow Start element listening on the eventSeparate transaction; publisher does not block or share limits
A complex reusable logic block is needed across multiple FlowsAuto-launched Flow called as a Subflow elementEncapsulates logic; parent maps variables; runs in same transaction
Flow logic must be retried on failure with no data lossPlatform Event + Flow (with fault path that re-publishes or logs)Async invocation decouples retries from the original transaction

Step-by-step instructions for an AI agent or practitioner activating this skill:

  1. Gather context — confirm the org edition, relevant objects, and current configuration state
  2. Review official sources — check the references in this skill's well-architected.md before making changes
  3. Implement or advise — apply the patterns from Core Concepts and Common Patterns sections above
  4. Validate — run the skill's checker script and verify against the Review Checklist below
  5. Document — record any deviations from standard patterns and update the template if needed

Review Checklist

Run through these before marking auto-launched Flow invocation work complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. Auto-launched Flows share the calling Apex transaction's governor limits — SOQL queries, DML statements, CPU time, and heap space consumed inside the Flow all count against the same 100-SOQL/150-DML limits as the Apex caller. A Flow that is well within limits when called in isolation can push a batch trigger transaction into a LimitException. Always budget Flow resource usage when designing Apex-invoked Flows.
  2. $Flow.FaultMessage is only available on the Fault path — If the Flow does not have a Fault connector on a failing element, the error propagates as an unhandled System.FlowException in the Apex caller. The fault message is not automatically logged. The best practice is to add a fault path on every DML/SOQL element that writes {!$Flow.FaultMessage} to a custom log object or sends an alert email. Without this, debugging production failures requires parsing generic exception messages.
  3. Flow API name is case-sensitive in Apex and REST calls — Flow.Interview.createInterview('my_flow', inputs) and Flow.Interview.createInterview('My_Flow', inputs) are different references. Salesforce stores Flow API names exactly as entered. A mismatch causes a runtime exception. Always copy the API name from Setup > Flows rather than typing it by hand.
  4. Output variables are null if start() throws or if the variable is not reachable — If the Flow execution path never assigns the output variable (e.g., the path was short-circuited by a Decision element), getVariableValue() returns null. Always null-check output values in Apex before casting or using them.
  5. Platform Event-triggered Flows run asynchronously with reset limits — Unlike Apex-invoked Flows, Platform Event-triggered Flows run in a new transaction. This means they cannot be controlled or blocked by the publisher, and their failures do not affect the publisher's transaction. However, it also means that any record Ids passed in the event payload must be re-queried inside the Flow — the publisher's in-memory state is not shared.

Output Artifacts

ArtifactDescription
Apex Flow.Interview invocation snippetCopy-paste Apex pattern with correct variable mapping, start(), and exception handling
Bulk collection invocation patternApex pattern for passing record collections to a Flow in a trigger or batch context
REST API request/response exampleHTTP request structure for calling a Flow from an external system
Fault path design recommendationGuidance for connecting fault paths and capturing {!$Flow.FaultMessage}

Package files