Skip to main content

Building Invocable Methods for Lightning Flow Integration

The @InvocableMethod annotation exposes an Apex method as an action in Flow Builder, making complex logic available to admins without code. When designed well, invocable methods become reusable building blocks that bridge Apex power with declarative ease. When designed poorly, they become brittle single-use endpoints that frustrate everyone.

Method Signature Requirements

An invocable method has strict signature rules:

  • Must be public static or global static
  • Must accept exactly one parameter: a List<T> where T is a primitive, SObject, or a class with @InvocableVariable properties
  • Must return void or a List<T> with the same type constraints
  • Only one @InvocableMethod is allowed per class
public class AccountRatingCalculator {

@InvocableMethod(
label='Calculate Account Rating'
description='Evaluates account metrics and assigns a rating (Gold, Silver, Bronze).'
category='Account Management'
)
public static List<Result> calculate(List<Request> requests) {
List<Result> results = new List<Result>();

for (Request req : requests) {
Result res = new Result();
res.rating = deriveRating(req.annualRevenue, req.openOpportunities);
res.ratingReason = buildReason(req.annualRevenue, req.openOpportunities);
results.add(res);
}

return results;
}

private static String deriveRating(Decimal revenue, Integer openOpps) {
if (revenue > 1000000 && openOpps > 5) return 'Gold';
if (revenue > 500000 || openOpps > 3) return 'Silver';
return 'Bronze';
}

private static String buildReason(Decimal revenue, Integer opps) {
return 'Revenue: ' + revenue + ', Open Opportunities: ' + opps;
}

// --- Input wrapper ---
public class Request {
@InvocableVariable(label='Annual Revenue' required=true)
public Decimal annualRevenue;

@InvocableVariable(label='Open Opportunity Count' required=true)
public Integer openOpportunities;

@InvocableVariable(label='Account ID' required=true)
public Id accountId;
}

// --- Output wrapper ---
public class Result {
@InvocableVariable(label='Calculated Rating')
public String rating;

@InvocableVariable(label='Rating Reason')
public String ratingReason;
}
}

@InvocableVariable Details

Each property on the wrapper class gets its own annotation:

AttributePurposeExample
labelDisplay name in Flow Builder'Annual Revenue'
descriptionTooltip help text'Total annual revenue in USD'
requiredWhether the Flow must populate ittrue or false

Properties must be public (not private with getters). Supported types include primitives (String, Integer, Decimal, Boolean, Date, Datetime, Id), SObject, and List<T> of these types.

Why Bulkification Matters

Flows typically call invocable methods with a single record per invocation. However, the platform may batch multiple Flow interviews into a single Apex invocation for performance. This is especially true in record-triggered flows where a bulk data load fires the flow for hundreds of records simultaneously.

Always process the full input list rather than assuming requests.size() == 1:

// Correct: processes the entire list
public static List<Result> calculate(List<Request> requests) {
List<Result> results = new List<Result>();
Set<Id> accountIds = new Set<Id>();

for (Request req : requests) {
accountIds.add(req.accountId);
}

// Single query for all accounts
Map<Id, Account> accountMap = new Map<Id, Account>(
[SELECT Id, Name FROM Account WHERE Id IN :accountIds]
);

for (Request req : requests) {
Result res = new Result();
res.rating = deriveRating(req.annualRevenue, req.openOpportunities);
results.add(res);
}

return results;
}

If you write logic assuming a single input, a bulk operation will either hit governor limits or produce incorrect results.

Labels and Descriptions: Your Admin's Documentation

The label, description, and category attributes on @InvocableMethod are the only documentation Flow admins see in Flow Builder. Invest in clear, specific labels:

  • label -- Action name in the palette (e.g., "Calculate Account Rating", not "DoCalc")
  • description -- What it does and any prerequisites (e.g., "Evaluates account metrics and assigns a rating. Requires Annual Revenue and Open Opportunity Count.")
  • category -- Groups the action in the palette (e.g., "Account Management")

Limitations and Workarounds

One method per class. If you need multiple invocable actions for the same domain, create separate classes. A common convention is [Domain][Action]Invocable (e.g., AccountRatingCalculator, AccountTerritoryAssigner).

No overloading. You cannot have multiple @InvocableMethod signatures in the same class.

Collections as inputs. To pass a list of records from a Flow, use List<SObject> or List<Id> as a property type on your request wrapper. Note that Flow collection variables map to List properties.

Return type alignment. The output list must have the same number of elements as the input list, in the same order. Each input element maps to its corresponding output element by index.

Key Takeaways

  • Design invocable methods as reusable, well-labeled building blocks -- not one-off utilities.
  • Always bulkify. The platform may batch multiple Flow interviews into one Apex call.
  • Use descriptive label, description, and category values -- they are the admin-facing API documentation.
  • One @InvocableMethod per class. Use separate classes for separate actions.
  • Return results in the same order and size as the input list.