SfSkills Domains Docs GitHub

Home / apex / apex-flow-invocation-from-apex

apex-flow-invocation-from-apex

Use when invoking Autolaunched Flows from Apex via `Flow.Interview.createInterview`. Covers parameter typing, output retrieval, governor boundaries, and when to inline logic instead. NOT for writing the `@InvocableMethod` class a Flow calls — use apex/invocable-methods. NOT for starting the same flow from the REST API, a Platform Event, or a subflow — use flow/auto-launched-flow-patterns.

Open the skill folder on GitHub


Apex Flow Invocation From Apex

Activates when Apex calls Flow.Interview.createInterview to run an Autolaunched Flow. Produces type-safe parameter maps, output retrieval, and clear guidance on the "Apex calls Flow" architectural choice.


Before Starting


Core Concepts

The Parameter Map Must Match Flow Variable Types Exactly

createInterview accepts Map<String, Object>. The keys are Flow input-variable API names. The values are Apex primitives, SObjects, or List<SObject>. Mismatched types throw Flow.FlowException at runtime with messages like "Type mismatch: expected Decimal, got String".

Collections in Flows have two flavors — SObject collections and primitive collections. Passing List<String> where the Flow expects a "Collection of Text" generally works; passing List<Opportunity> where it expects a "Collection of SObject Records" requires the Flow's SObject type to match.

Starting The Interview Runs The Flow

After createInterview, call .start(). This synchronously runs the Flow to completion (for Autolaunched Flows without pause elements). Long-running Flows — those with loops that create/update many records — count every action against the caller's governor limits.

Output Variables Come Back By Name

After .start(), retrieve outputs with interview.getVariableValue(name). The return type is Object; cast to the expected type. A typo in the output name returns null — no exception.

Invocable Actions Are A Better Fit For Dynamic Invocation

If the caller doesn't know which Flow to run until runtime, consider registering the Flow as an Invocable Action and using the Action framework. This gives you discovery, validation of required parameters, and a better error surface than raw Flow.Interview.


Common Patterns

Pattern 1: Invoke An Autolaunched Flow With Typed Inputs And Outputs

When to use: Apex must delegate a calculated business rule that an admin maintains in Flow.

How it works:

public with sharing class TierAssignmentService {
    public static String assignTier(Account a, Decimal yearToDate) {
        Map<String, Object> params = new Map<String, Object>{
            'inputAccount'    => a,
            'yearToDateAmount'=> yearToDate
        };
        Flow.Interview i = Flow.Interview.createInterview('Assign_Account_Tier', params);
        i.start();
        return (String) i.getVariableValue('resultTier');
    }
}

Why not the alternative: Hardcoding the tier logic in Apex forces deploys for admin-owned tweaks. Calling the Flow preserves declarative ownership while giving Apex a clean call site.

Pattern 2: Bulk-Safe Flow Invocation

When to use: A trigger fires for 200 records and each needs the Flow's logic.

How it works:

public with sharing class BulkTierAssignment {
    public static void assignAll(List<Account> accounts, Map<Id, Decimal> ytdById) {
        Map<String, Object> params = new Map<String, Object>{
            'inputAccounts' => accounts,
            'ytdMap'        => ytdById
        };
        Flow.Interview i = Flow.Interview.createInterview('Bulk_Assign_Tier', params);
        i.start();
    }
}

Why not the alternative: Calling createInterview in a per-record loop multiplies CPU consumption and makes failure isolation harder. Design the Flow to accept a collection and iterate internally.


Decision Guidance

SituationRecommended ApproachReason
Admin-owned decision table (tier, routing)Apex calls FlowAdmin maintains, Apex integrates
Hot-path computation in a triggerInline ApexFlow adds overhead for simple math
Dynamic flow selection at runtimeInvocable Action frameworkAdds discovery, validation, error surface
Flow with screen elementsDo not invoke from ApexScreen Flows cannot run headless
Long-running orchestration with waitsFlow Orchestrator, not direct Apex callOrchestrator handles waits; Flow.Interview does not

  1. Confirm the Flow is Autolaunched (Flow Builder → Start element → "Autolaunched Flow").
  2. Read the Flow's input and output variable API names and their Apex-equivalent types.
  3. Build a Map<String, Object> with typed keys/values.
  4. Call Flow.Interview.createInterview('Flow_API_Name', params) — confirm exact API name.
  5. Wrap .start() in try/catch for Flow.FlowException (flow-internal failures) and DmlException (for governor exhaustion).
  6. Read outputs via getVariableValue(name) with a cast.
  7. Add a test with @IsTest that arranges realistic input and asserts the output; the Apex wrapper still needs its own coverage.

Review Checklist


Salesforce-Specific Gotchas

See references/gotchas.md for the full list.

  1. Screen Flows fail at runtime — only Autolaunched Flows start from Apex.
  2. Typo in Flow API name throws at runtime, not compile time.
  3. Governor limits are charged to the calling transaction, not reset by the Flow boundary.
  4. Output variable typo returns null silently.
  5. Picklist values must match Flow's expected internal value, not the label.

Output Artifacts

ArtifactDescription
references/examples.mdAutolaunched Flow invocation, bulk-safe collection pattern
references/gotchas.mdScreen Flow failures, governor-limit sharing
references/llm-anti-patterns.mdCommon LLM mistakes: hardcoded per-record loops, wrong parameter types
references/well-architected.mdOps framing: admin ownership vs deploy cadence
scripts/check_apex_flow_invocation_from_apex.pyStdlib lint for per-record invocation and missing try/catch

Package files