InvocableMethod Annotation

Use the InvocableMethod annotation to identify methods that can be run as invocable actions.

If a flow invokes Apex, the running user must have the corresponding Apex class security set in their user profile or permission set.

Note

Invocable methods are called natively from Rest, Apex, Flow, Agentforce agents or Einstein bots that interacts with the external API source. Invocable methods have dynamic input and output values and support describe calls.

This code sample shows an invocable method with primitive data types.

public class AccountQueryAction {
  @InvocableMethod(label='Get Account Names' description='Returns the list of account names corresponding to the specified account IDs.' category='Account')
  public static List<String> getAccountNames(List<ID> ids) {
    List<Account> accounts = [SELECT Name FROM Account WHERE Id in :ids];
    Map<ID, String> idToName = new Map<ID, String>();
    for (Account account : accounts) {
      idToName.put(account.Id, account.Name);
    }
    // put each name in the output at the same position as the id in the input
    List<String> accountNames = new List<String>();
    for (String id : ids) {
      accountNames.add(idToName.get(id));
    }
    return accountNames;
  }
}

This code sample shows an invocable method with a specific sObject data type.

public class AccountInsertAction {
  @InvocableMethod(label = 'Insert Accounts' description='Inserts the accounts specified and returns the IDs of the new accounts or null if account is failed to create.' category = 'Account')
    public static List<ID> insertAccounts(List<Account> accounts) {
      Database.SaveResult[] results = Database.insert(accounts, false);
        List<ID> accountIds = new List<ID>();
        
        for (Database.SaveResult result : results) {
          if (result.isSuccess()) {
            accountIds.add(result.getId());
          } else {
            accountIds.add(null);
            }
        }
        
    return accountIds;
  }
}

This code sample shows an invocable method with the generic sObject data type.

public with sharing class GetFirstFromCollection {
  @InvocableMethod
  public static List<Results> execute (List<Requests> requestList) {
    List<Results> results = new List<Results>();
    for (Requests request : requestList) {
      List<SObject> inputCollection = request.inputCollection;
      SObject outputMember = inputCollection[0];
      
      //Create a Results object to hold the return values
      Results result = new Results();
      
      //Add the return values to the Results object
      result.outputMember = outputMember;
      
      //Add Result to the results List at the same position as the request is in the requests List
      results.add(result);
    }
    return results;
  }

  public class Requests {
    @InvocableVariable(label='Records for Input' description='yourDescription' required=true)
    public List<SObject> inputCollection;
  }

  public class Results {
    @InvocableVariable(label='Records for Output' description='yourDescription' required=true)
    public SObject outputMember;
  }
}

This code sample shows an invocable method with a custom icon from an SVG file.

global class CustomSvgIcon { 
  @InvocableMethod(label='myIcon' iconName='resource:myPackageNamespace__google:top')
  global static List<Integer> myMethod(List<Integer> request) {
    List<Integer> results = new List<Integer>();
    for(Integer reqInt : request) { 
       results.add(reqInt);
    }
    return results;
  }
}

This code sample shows an invocable method with a custom icon from the Salesforce Lightning Design System (SLDS).

public class CustomSldsIcon { 
  
  @InvocableMethod(iconName='slds:standard:choice') 
  public static void run() {} 
  
  }

To handle exceptions within an invocable method, wrap the results in an Apex object that reports failures. The execution of the invocable method must run and return the same number of results as inputs received even if errors occur.

For example, this code sample adjusts positive values by taking their square root and multiplying by pi, setting a success flag to true. For negative values, it sets the success flag to false.

global class AdjustPositiveValuesAction {
  @InvocableMethod(label='Adjust Positive Values' description='Returns the list of adjusted values. If a number is negative, a failure is reported for that value.')
  
  public static List<AdjustmentResult> doAdjustment(List<Double> values) {
    List<AdjustmentResult> results = new List<AdjustmentResult>();
    
    for (Double value : values) {
    AdjustmentResult result = new AdjustmentResult();
  
    try {
      // Adjust the value, scale by pi.
      // Note: If the value is negative, this operation throws an exception.
      result.adjustedValue = Math.sqrt(value) * Math.PI;
      result.adjustmentSucceeded = true;
    }
    catch (Exception e) {
      // If a negative value caused an exception, mark the adjustment as failed, and keep processing other values.
      result.adjustmentSucceeded = false;
    }
  
    results.add(result);
  }
  
  return results;
}
  
global class AdjustmentResult {
  @InvocableVariable(label='True if adjustment succeeded')
  global boolean adjustmentSucceeded;
 
  @InvocableVariable(label='Adjusted value, only valid if adjustment succeeded')
  global Double adjustedValue;
  }
}

This test method checks whether the value adjustments were successful and verifies the calculated values for positive inputs.

// Test class for AdjustPositiveValuesAction
@isTest
private class AdjustPositiveValuesActionTest {
  private static testMethod void doTest() {
    // Create a list of test values: 4, -1, 1
    List<Double> values = new List<Double>();
    values.add(4);
    values.add(-1);
    values.add(1);
  
    Test.startTest();
 
     // Call the doAdjustment method with the test values.
    List<AdjustPositiveValuesAction.AdjustmentResult> results = AdjustPositiveValuesAction.doAdjustment(values);
 
    Test.stopTest();
 
    // Assertions to check if adjustments were successful or not for each input value.
    system.assertEquals(true, results[0].adjustmentSucceeded);
    system.assertEquals(false, results[1].adjustmentSucceeded);
    system.assertEquals(true, results[2].adjustmentSucceeded);
 
    // Assertions to check the calculated adjusted values for positive inputs.
    system.assertEquals(2 * Math.PI, results[0].adjustedValue);
    system.assertEquals(Math.PI, results[2].adjustedValue);
  }
}

Supported Modifiers

All modifiers are optional.
label
The label for the method, which appears as the action name in Flow Builder. The default is the method name, though we recommend that you provide a label.
description
The description for the method, which appears as the action description in Flow Builder. The default is Null.
callout
The callout modifier identifies whether the method calls to an external system. If the method calls to an external system, add callout=true. The default value is false.
capabilityType
The capability that integrates with the method. The valid format is Name://Name, for example: PromptTemplateType://SalesEmail
category
The category for the method, which appears as the action category in Flow Builder. If no category is provided (by default), actions appear under Uncategorized.
configurationEditor
The custom property editor that is registered with the method and appears in Flow Builder when an admin configures the action. If you don’t specify this modifier, Flow Builder uses the standard property editor.
iconName
The name of the icon to use as a custom icon for the action in the Flow Builder canvas. You can specify an SVG file that you uploaded as a static resource or a Salesforce Lightning Design System standard icon.

InvocableMethod Considerations

Implementation Notes
  • The invocable method must be static and public or global, and its class must be an outer class.
  • Only one method in a class can have the InvocableMethod annotation.
  • Other annotations can’t be used with the InvocableMethod annotation.
Inputs and Outputs
There can be at most one input parameter and its data type must be one of the following:
  • A list of a primitive data type or a list of lists of a primitive data type – the generic Object type isn’t supported.
  • A list of an sObject type or a list of lists of an sObject type.
  • A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).
  • A list of a user-defined type, containing variables of the supported types or user-defined Apex types, with the InvocableVariable annotation. To implement your data type, create a custom global or public Apex class. The class must contain at least one member variable with the invocable variable annotation.
If the return type isn’t Null, the data type returned by the method must be one of the following:
  • A list of a primitive data type or a list of lists of a primitive data type – the generic Object type isn’t supported.
  • A list of an sObject type or a list of lists of an sObject type.
  • A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).
  • A list of a user-defined type, containing variables of the supported types or user-defined Apex types, with the InvocableVariable annotation. To implement your data type, create a custom global or public Apex class. The class must contain at least one member variable with the invocable variable annotation.

    For a correct bulkification implementation, the Inputs and Outputs must match on both the size and the order. For example, the i-th Output entry must correspond to the i-th Input entry. Matching entries are required for data correctness when your action is in bulkified execution, such as when an apex action is used in a record trigger flow.

    Note

Managed Packages
  • You can use invocable methods in packages, but after you add an invocable method you can’t remove it from later versions of the package.
  • Public invocable methods can be referred to by flows and processes within the managed package.
  • Global invocable methods can be referred to anywhere in the subscriber org. Only global invocable methods appear in Flow Builder and Process Builder in the subscriber org.

For more information about invocable actions, see the Actions Developer Guide.