How Apex Actions Supercharge Your Salesforce Flows
Since its introduction, Salesforce Flow has provided an easy, drag-and-drop way to automate Salesforce business processes. Historically though, this ease of use came with a trade-off. A Flow was quick and intuitive to set up, but its capabilities were limited to straightforward use cases.
To satisfy more-complex automation challenges, admins either turned to a different declarative tool or came crawling to surly developers to create Apex Triggers.
With consistent feature improvements and steadily advancing web technology (think Flash to HTML5), Salesforce has filled many of these gaps that once turned admins away from Flows. Some improvements include triggering a Flow from a record delete, sending outbound messages, and debugging Flows as a specific user.
Despite these improvements, shortfalls remain, and Flow continues to lag in a couple of major areas, such as:
- Performing complex business logic. Attempting to address complex logic in a Flow can result in a hard-to-decipher Spaghetti Flow.
- Avoiding Governor Limits In a Flow with one or more loops, it can be easy to run afoul of certain limitations, like having 2000 executed elements at run time.
Thankfully, Salesforce gives us a way to fill these gaps while still taking advantage of the simplicity of Flows.
Leverage the Power of Apex from Flow Builder
By tapping into Apex code, we can plug the gaps in Flow capabilities. In this case, though, we aren’t talking about a mechanism that lives entirely outside of a Flow, like an Apex Trigger.
Instead, Salesforce provides a way to make custom Apex code accessible entirely within admin-centric areas. Using tools like Flow Builder, we can then delegate complex tasks to code and handle the heavy lifting using programmatic power.
The Salesforce technology that makes Apex available from these tools is Invocable Actions.
Invoke Apex from a Flow
With Invocable Actions, a developer creates code once and designates it for reuse by other tools in the Salesforce toolkit. Some places where an Invocable Action can be re-used are:
- Flow Builder (the focus of this post).
- Next Best Action.
- Apex classes. Recently became GA available in the Winter ’23 release.
We can even package Invocable Actions and make them available on the Salesforce AppExchange. For example, Salesforce Labs has a package of Flow Actions available here: https://sforce.co/3fH5dO5.
Other companies and developers beyond Salesforce Labs also make Flow Actions available on the AppExchange.
How Apex Code Becomes an Invocable Action
By using the @InvocableMethod and @InvocableVariable annotations, an Apex Class becomes an Invocable Action and is then available in the Flow Builder palette as an Apex Action.
That’s a lot of Actions. To summarize:
- Apex Code becomes an Invocable Action through specific annotations.
- The Apex Action element is what lets us tie to the Invocable Action from Flow Builder.
The terminology can get confusing, but you don’t really need to know all of that to make it useful.
Just know that once you have an Invocable Action, anyone designing a Flow can drag it (in the form of an Apex Action element) onto the Flow Builder canvas, pass in inputs from the Flow to Apex code, and assign outputs from the Apex Action back to Flow variables.
The Apex Action can be used multiple times in a single Flow or across different Flows.
How to Create an Invocable Action
First, create an Apex class with an Invocable Method focused on a single work item. An example of this work could be updating all Tasks associated with an Opportunity or connecting to a third-party API.
The recommendation for Invocable Action naming is to use an action + object format. So, if your Invocable Action lists an item for sale on eBay, your class name might be Post Listing to eBay (PostListingToEBayAction).
The two steps that transform a run-of-the-mill Apex Class into an Invocable Action are:
- Annotate a class method with the @InvocableMethod interface.
You can have more than one method in your class, but only one method can be annotated as Invocable. This method takes a single input parameter. - Designate variables for input and output with the @InvocableVariable interface.
A common pattern for this is to use standalone inner classes — 1 for the input variables and 1 for the output variables. This allows you to pass multiple parameters back and forth between your Flow and Apex by designating the member variables of your inner classes as Invocable.
Apex Actions in a Flow: Step by Step
We’ll start by showing how to build an Invocable Action. Next, we’ll provide a sample Flow that uses an Apex Action element to delegate processing to the Invocable Action.
Invocable Action Example
In the simple Invocable Action below, we take in a list of accounts as input parameters (even if just one account is passed from the Flow) and return a list of deleted task names.
public class DeleteAccountTasksAction {
@InvocableMethod(label='Delete Child Tasks' description='Deletes child tasks associated with the parent record and returns the list of deleted task names')
public static List<OutputData> deleteAccountTasks(List<InputData> inputData){
List<Account> accountsInputList = inputData.get(0).accounts;
List<String> deletedTaskNames = new List<String>();
Set<Id> accountIds = new Set<Id>();
for(Account account : accountsInputList){
accountIds.add(account.Id);
}
List<Task> taskList = [SELECT Subject FROM Task WHERE WhatId IN :accountIds];
for(Task task : taskList){
deletedTaskNames.add(task.Subject);
}
delete taskList;
List<OutputData> outputDataList = new List<OutputData>();
OutputData outputData = new OutputData();
outputData.taskNames = deletedTaskNames;
outputDataList.add(outputData);
return outputDataList;
}
public class InputData{
@InvocableVariable
public List<Account> accounts;
}
public class OutputData{
@InvocableVariable
public List<String> taskNames;
}
}
The label on the Invocable Action displays in Flow Builder when searching for and selecting an Apex Action. In this case, “Delete Child Tasks” (the label) displays in the New Action search screen in Flow Builder.
Our Invocable Action takes an inner class called InputData as a List parameter and outputs an inner class called OutputData as a returned List.
public static List<OutputData> deleteAccountTasks(List<InputData> inputData)
Later, from our Flow, we will use the InputData parameter to pass a List of Accounts to the deleteAccountTasks method. Then, we will assign the return value of OutputData back to a Flow variable.
The Invocable Action Inner Classes
We have one inner class set up for input and another set up for output.
public class InputData{
@InvocableVariable
public List<Account> accounts;
}
public class OutputData{
@InvocableVariable
public List<String> taskNames;
}
These are basic classes, and we don’t really need them for our simple use case, but we included them here to illustrate the structure you could use to populate and return multiple variable values.
Each of our inner classes has a single variable annotated with @InvocableVariable. This annotation makes the variable available for input or output in our Flow.
To populate multiple variables as input or output, you would add another variable in the appropriate class and annotate it with @InvocableVariable. Here’s what that would look like for the InputData class.
public class InputData{
@InvocableVariable
public List<Account> accounts;
@InvocableVariable
public String accountSearchTerm;
}
Here, we added an additional Invocable Variable called “accountSearchTerm” for example purposes. We won’t be using this variable in our Flow.
Retrieving Input Data from a Flow
To populate values in our Apex Class via input parameters, we reference the first element in List<InputData> and then get the list of Accounts from that InputData List item.
List<Account> accountsInputList = inputData.get(0).accounts
Populating Output Data from an Invocable Action to a Flow
It’s equally simple to return data from Invocable Apex to our Flow. To do that, we create a single instance of our OutputData inner class and populate its taskNames member variable. We then add this class instance to the List<OutputData> and return it from our Invocable Method.
List<OutputData> outputDataList = new List<OutputData>();
OutputData outputData = new OutputData();
outputData.taskNames = deletedTaskNames;
outputDataList.add(outputData);
return outputDataList;
That’s it for the Apex. Again, this is a very simple use case and something you could accomplish with a Flow on its own, but we wanted to keep things simple for this example. Now, on to Flow Builder.
Using the Apex Action in Flow Builder
Here are the high-level details of our Flow:
- Record-triggered Flow on the Account object. Entry criteria are met when the active flag on the Account is set to No.
- Assign the triggering Account to a Flow variable containing a collection of Accounts.
- Find and configure the Delete Account Tasks Apex Action. Populate the accounts Invocable Variable with the Flow variable containing the collection of Accounts.
- Assign returned data from the Apex Action to a Flow variable.
Setting up the Record-triggered Flow
We set our Flow to run any time an Account is updated. The entry conditions are met when the Active field is changed to “No”. The Flow only runs when the record is updated to meet those conditions.
Create the Flow Collection Variable
We create a new Flow variable containing a collection of Account records and assign our triggering Account record to the collection.
Configuring the Apex Action
Select the Action element from the Add Element screen in Flow Builder.
From the New Action screen, search for the Action by its label name and select it.
Once our Invocable Action is chosen, the configuration screen for the Apex Action appears. We can add a Label, API Name, and Description for the Apex Action. Then, we set input values, assigning our collection of Accounts to the accounts List from our Invocable Apex.
We also manually assign data returned from our Invocable Apex to Flow variables. In our simple use case, there is no reason to return any data, so we leave that option unselected.
Select the Done button. We have now configured the Apex Action. From here, we can debug our Flow by passing in a specific Account and verifying that the associated Tasks were deleted.
Summary of Using Apex Actions in a Flow
- An Apex Class with the @InvocableMethod and @InvocableVariable annotations becomes an Invocable Action.
- The Invocable Action is available in Flow Builder as an Apex Action.
- The Apex Action can be added to the Flow Builder canvas and provided with inputs from the Flow.
- From the Apex Action configuration screen, assigned data returned from the Invocable Action to Flow variables.
- Use the Invocable Action from other declarative tools and Apex classes.
That’s it. Hopefully, you have a better appreciation of the potential of Apex Actions and Salesforce Flows. Now, it’s your turn to build something.
So go out and take Action! (hahaha …. )