Invocable Methods: How to Send Data Between Flow and Apex

Invocable methods used with Flow allow you to launch something in an admin friendly format that uses the massive power of Apex. For example, you have an intake screen that collects answers to a few questions, then you use Apex to loop through many related records dispersing those answers in places hard to reach from Flow.

Creating an invocable method in a nutshell: First you write an apex class with @invocable method (label and description) and whatever code you want the apex to do (easy, right?) Then make your Flow including your input and output variables. Then add an Apex action in Flow to send/receive those variables.

Here are some things I learned about sending data between Flow and Apex.

Invocable methods in Apex always receive a List and they return a List unless the return type is null. Read more under “Inputs and Outputs” here.

SentSent FromReceived by Received
Record Variable.idFlowApexList<Id> listOfIds
Record Collection VariableFlowApexList<List<Opportunity>> nameOfThis
List containing 1 sObject recordApexFlowRecord (single) variable
List of Lists of sObjectApexFlowRecord Collection Variable

This is NOT an exhaustive list at all. I didn’t try sending a record variable (not just the ID from Flow), but I assume that will work. There are also generic sObjects that are pretty special, but I didn’t try.

Here are two examples of invocable methods used with Flows. Note that what you send to apex does not impact what you can receive, as far as I know.

Example One: Send One Id, Get Back a Record Collection

In this Flow, I tried using an Apex Action to avoid having a DML statement inside a loop. You may notice that there is also a loop inside another loop. So this Flow is not an example I want you to go out and copy (I ended up moving the whole thing to Apex), but I got the Apex Action to work and I learned a ton!
  • I sent a record id stored in a record variable from Flow to Apex.
  • The apex receives it as a List of Ids, even though it is just one id. (The apex input parameter is (List <Id> nameOfList)
  • To return a List of Lists start by making a list of an sObject type. [The next part is just an example. Substitute whatever code fits your needs.] I used SOQL to make a list for all Opportunities whose Primary Contact is the recordId sent from the Flow.
  • Add that List to a “List of Lists” of that same sObject type (yeah, weird, right?)
  • So my original List has one item, and my List of Lists has only one List. You with me?
  • My method returns that List of Lists of Opportunities and the Flow receives it as a Collection Variable!
This image is from the Apex Action inside the Flow.
public class contactsListAction {
    //this invocable method takes a single contact id from Flow from a record variable
    //and returns a list of lists of opportunities associated with that contact.
    //the list of lists is stored as a collection variable in the Flow.
    @InvocableMethod (label = 'SendContactsGet Opps' description = 'returns opps for this contact.')
    
    //this method returns a List of Lists of Opps to the flow and receives a List of Ids
    public static List<List<Opportunity>> getContactIds (List<ID> ids) {
        
        //Store in a list the id, closedate and Amounts of opps whose Primary Contact came in the variable "ids"
        //and who have a stage of Clsoed Won and a Close Date of this year
        List<Opportunity> opps = [SELECT id,CloseDate,Amount
                                  FROM Opportunity                                   
                                  WHERE npsp__Primary_Contact__c in :ids 
                                  AND StageName = 'Closed Won' 
                                  AND CloseDate = THIS_YEAR]; 
        //declare a new list of lists of opps
        List<List<Opportunity>> itemListList = new List<List<Opportunity>>();
        //add the list opps to the list of lists
        itemListList.add(opps);
        // send list of lists to the Flow	
        return itemListList;        
    }  
}

Example Two: Send a Collection, Receive Back One Record

  • I sent a record collection variable from the Flow to Apex.
  • The apex receives it as a List of Lists of type Opportunity.
  • [This part is just an example so can substitute whatever code you want here.] I’m finding the first List in the List of Lists, then finding the first Opp in that List and saving that as an opportunity record. I modify the value of the opp here.
  • I now have one opportunity record that I need to add to a new list of Opportunities.
  • I return that List (that just has one item in it) to the Flow.
public class sendManyGetOne {
    @InvocableMethod (label = 'Send Many Get One' description = 'send a collection of records to Apex, get one record back.')
    
    //this method returns a List containing one opportunity 
    //the parameters are a List of Lists of Opportunities. The flow sends this as a collection variable of opps.
    public static List<Opportunity> getOppId ( List<List<Opportunity>> listOppLists) {
        
        //get the first List from the List of Lists
        List<Opportunity> oppsList = listOppLists.get(0);
        
		//get the id and Amount from the Opp in that first List. 
        List<Opportunity> opps = [SELECT id,Amount
                                  FROM Opportunity                                   
                                  WHERE id in :oppsList]; 
        //convert that to an opportunity
        Opportunity newOpp = opps.get(0);
        //change a value on it
        newOpp.Amount = 800;
        //instantiate a new list 
        List <Opportunity> returnOpps = new List <Opportunity> ();
        //add that modified opp to the new list
        returnOpps.add(newOpp);
        //send the new list of just one opp back to the Flow
        return returnOpps;
	}
}

Learn More

Official Documentation on Invocable Methods.

Published by

JessieRymph

Jessie joined Salesforce.org in 2018 to give introductory webinars to nonprofit customers. She now is a Senior Solution Developer supporting nonprofits and education customers at Salesforce. All opinions expressed on this blog are her own or those of the contributors. She's spent 17 years more or less in CRMs and databases, but didn't meet Salesforce until 2011. Jessie co-led the Seattle Salesforce Nonprofit User Group in 2015-2016. She wrote a sh*tty first draft of a novel and hopes to turn it into a screenplay!

14 thoughts on “Invocable Methods: How to Send Data Between Flow and Apex”

  1. This is extremely useful, thank you so much! I find it weird that when I wish to send a collection, the matching datatype in Apex has to be a collection of collections. And it is not exactly properly documented by Salesforce anywhere, or at least I could not find it. Thanks for this.

    1. Exactly! I write most of my posts with the documentation could use some additional help 🙂

  2. Thank you ! This is very helpful to write an APEX action. How can we write the test class for the same? Can you please show one example of Test class where we are passing a collection of string (emails) to the APEX class.

  3. Priyesh, what are you doing with the emails? I think the test would trigger the flow, and then check for the results just like anything else. I don’t think it really matters what happens in the middle regarding getting the emails into the class.

    1. I’m sending HTML email to contacts using APEX action. I’m getting a list of list of email addresses into the APEX action as variable. Based on contacts geo location I’m doing some template selection and Org wide address selection for sending email in the apex class. I’m not able to write a test class for the same. How do I pass test data(test email addresses) to List of List collection variable in flow in test class?

      1. If it’s a record triggered flow, you create test data in your test, and launch the flow from your test. Then you confirm the results. If you are testing just the apex part, you could create a list of list of strings like this:
        //declare new list of strings
        List listofStrings = new List {‘jon@bob.com’,’jonny@bobby.com’};
        //declare a new list of lists of strings
        List> stringListList = new List>();

        //add the list Strings to the list of lists
        stringListList.add(listOfStrings);

  4. Hello – Something to consider is that (unlike Flow) Apex requires that you bulkify the entry of data into an Apex method. That’s why if you want to process ONE object (eg, an Account record) in an Apex method, it MUST ACCEPT a LIST (eg, a list of Account records). Imagine loading 1000 objects into Salesforce using Data Loader and they ALL call your method. The system will process them in batches of 200 through your method — and that’s why your method MUST accept a list. This list will hold those 200 objects. Likewise, if your method is designed to receive and process a list (eg, a list of Account records) then your method MUST ACCEPT a List of Lists (so that it can batch process 200 lists at a time). As far as sending data back to Flow, you must always send back a list (or a List of lists). However, if (for example) you send a list of Accounts back WITH ONLY ONE record in it, and you assign it to a SINGLE RECORD variable back in flow — it will accept it. But if you try to send a list back that contains TWO or MORE records (objects) the flow will likely fail if you try to assign it to a single record variable. Anyway, I’m pretty new to this myself, so I’m glad to have found your blog! Thanks!

    1. Thanks so much for sharing! Is this contradicting anything I said? Or do you suggest I update it with this info? This stuff is hard to keep track of!

      1. Hi Jessie — I think the article should be updated to state up front that an Apex invocable method must always accept a list and return a list (unless the return type is void). This is well documented in Salesforce here, under the heading “Inputs and Outputs”: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_InvocableMethod.htm

        You could still use the table to reflect what the input/output lists would look like based on what data you were passing between the method and Flow (eg, if you passed a collection (list) of Account records to the method, your method must receive a list of lists). As far as the bulkification of data entering a method, this is not unlike receiving the Trigger.new in an Apex trigger. Even if one record is being processed, you will always receive a list of records — a list that you then must process in your code (even if it only contains one item).

  5. Thank you! I have made that update. This is a really good point regarding triggers. I appreciate your help.

  6. Going off bulkification – these examples will not work for situations in bulk if they’re on a record-triggered flow

    The reason you accept a List of Ids in your method even though in Flow you only pass one Id is because of bulkification as the other comment mentioned.

    If this is a record-triggered flow – there’s a different flow interview for different records in the same transaction. Yet, the invocable apex method is only called once (all Ids get passed in).

    The key in that bulk scenario is that your invocable method needs to return the same number of outputs as inputs. Ex. 6 contact Ids in input, expects a list of 6 lists. One list for each input. Even if the query returns null, you’d have to put an empty list to signify that Id returned no records.

Leave a Reply