Here is the simplest way I have seen to use invocable methods to send data from flow to apex and apex to flow. This is a huge improvement over what I wrote in 2021.
The trickiest thing to understand here is that an output variable in Flow is an INPUT variable in Apex. It goes OUT from the Flow and IN to your Apex. I have some charts to help clarify what goes in and what comes out.
Some caveats: These are not examples of when to use Flow and Apex together, but just how it can be done. Also, I am a newer developer and I am just figuring this stuff out. If you see ways to improve, let me know!
Thanks to @jonsayer for showing me this method while working on the latest Unsubscribe Link (coming soon)!
EXAMPLE ONE
Get an Account Id, send it as a text variable from the Flow to Apex, return all the opportunities on that account to the flow, and update them in the Flow.

public with sharing class mostBasicClass {
@InvocableMethod(
label='Test Sending one Variable'
description='test sending and receiving with Flow'
)
public static List<FlowOutputs> receiveInput(List<FlowInputs> input) {
List<FlowOutputs> results = new List<FlowOutputs>();
for (FlowInputs i : input) {
FlowOutputs theOutputs = new FlowOutputs();
theOutputs.var_Output = myMethod(i.var_Input);
results.add(theOutputs);
}
return results;
}
public static List<Opportunity> myMethod(String var_Input) {
List<Opportunity> var_Output = [
SELECT Id
FROM Opportunity
WHERE AccountId = :var_Input_AccountId
];
return var_Output;
}
//input variables that come to apex from flow
public class FlowInputs {
@InvocableVariable
public String var_Input;
}
//output variables that go from Apex to flow
public class FlowOutputs {
@InvocableVariable
public List<Opportunity> var_Output;
}
}
The two charts below explain how each variable is classified in both Flow and Apex. Flow variables are designated as as Available for Input or Available for Output when creating them.
Send AccountID from Flow to Apex
| Direction | Variable Name | Variable Format | |
| Flow | Output | var_text | text |
| Apex | Input | var_Input | String |
Send Opportunities from Apex to Flow
| Direction | Variable Name | Variable Format | |
| Flow | Input | incomingOppsList | record collection variable (opportunities) |
| Apex | Output | var_Output | List of opportunities |

Add an action to the canvas and find your invocable method. Assign the Flow variables to the Apex variables. Variables coming IN to the flow need to be assigned under advanced options.

EXAMPLE TWO
Send a single opportunity record and a collection of opportunities to Apex. Return an integer and a list of Opportunities to update in Flow. There are three versions of the code below – 1 bare bones, 2 with comments, 3 with an example instead of //do the thing.

Send Opportunity Record From Flow to Apex
| Direction | Variable Name | Variable Format | |
| Flow | Output | Opportunity from get opp | record variable |
| Apex | Input | rec_ToApexOpp | Opportunity |
Send Record Collection From Flow to Apex
| Direction | Variable Name | Variable Format | |
| Flow | Output | Opportunities from get Opportunities | record collection variable |
| Apex | Input | list_toApexOpps | List of Opportunities |
Send Integer from Apex to Flow
| Direction | Variable Name | Variable Format | |
| Flow | Input | var_NumberOfOpps | Number |
| Apex | Output | into_ToFlow | Integer |
Send Opportunity List from Apex to Flow
| Direction | Variable Name | Variable Format | |
| Flow | Input | incomingOppsList | record collection variable |
| Apex | Output | list_ToFlowOpps | List of Opportunities |

EXAMPLE TWO WITHOUT COMMENTS
public class sampleApexClass {
public static Integer numberOfOpportunities;
@InvocableMethod(
label='Test Sending Variables'
description='test sending and receiving with Flow'
)
public static List<FlowOutputs> receiveInput(List<FlowInputs> input) {
List<FlowOutputs> results = new List<FlowOutputs>();
for (FlowInputs i : input) {
FlowOutputs theOutputs = new FlowOutputs();
theOutputs.list_toFlowOpps = dotheThing(
i.rec_toApexOpp,
i.list_toApexOpps
);
theOutputs.int_toFlow = numberOfOpportunities;
results.add(theOutputs);
}
return results;
}
public static List<Opportunity> doTheThing(
Opportunity rec_toApexOpp,
List<Opportunity> list_toApexOpps
)
//do something
…
return list_toFlowOpps;
}
//input details that comes to apex from flow
public class FlowInputs {
@InvocableVariable
public Opportunity rec_toApexOpp;
@InvocableVariable
public List<Opportunity> list_toApexOpps;
}
//output details that go from Apex to flow
public class FlowOutputs {
@InvocableVariable
public List<Opportunity> list_toFlowOpps;
@InvocableVariable
public Integer int_toFlow;
}
}
EXAMPLE TWO WITH COMMENTS
public class sampleApexClass {
//optional: a class variable to hold the integer I will return to the Flow.
public static Integer numberOfOpportunities;
//declare the invocable method.
@InvocableMethod(
label='Test Sending Variables'
description='test sending and receiving with Flow'
)
//this method will receive a list of flowinputs and return a list of flowoutputs
public static List<FlowOutputs> receiveInput(List<FlowInputs> input) {
List<FlowOutputs> results = new List<FlowOutputs>();
for (FlowInputs i : input) {
FlowOutputs theOutputs = new FlowOutputs();
//from here you start adding your outputs
//theOutputs dot one of your output variables (list_toFlowOpps)
//here my list = the list returned from the doTheThing method
theOutputs.list_toFlowOpps = dotheThing(
//I pass the inputs to the method
i.rec_toApexOpp,
i.list_toApexOpps
);
//for my next output, I can set its value directly here
theOutputs.int_toFlow = numberOfOpportunities;
results.add(theOutputs);
}
return results;
}
public static List<Opportunity> doTheThing(
Opportunity rec_toApexOpp,
List<Opportunity> list_toApexOpps
) {
//use the variables to do something
...
numberOfOpportunities = list_toFlowOpps.size();
return list_toFlowOpps;
}
//list each flowinput with @InvocableVariable above it
public class FlowInputs {
@InvocableVariable
public Opportunity rec_toApexOpp;
@InvocableVariable
public List<Opportunity> list_toApexOpps;
}
//list each flowOutput with @InvocableVariable above it
public class FlowOutputs {
@InvocableVariable
public List<Opportunity> list_toFlowOpps;
@InvocableVariable
public Integer int_toFlow;
}
}

EXAMPLE TWO – COMMENTS, FULLY WORKING CODE
public class sampleApexClass {
//optional:a class variable to hold the integer I will return to the Flow.
public static Integer numberOfOpportunities;
//declare the invocable method.
@InvocableMethod(
label='Test Sending Variables'
description='test sending and receiving with Flow'
)
//this method will receive a list of flowinputs and return a list of flowoutputs
public static List<FlowOutputs> receiveInput(List<FlowInputs> input) {
List<FlowOutputs> results = new List<FlowOutputs>();
for (FlowInputs i : input) {
FlowOutputs theOutputs = new FlowOutputs();
//from here you start adding your outputs
//theOutputs dot one of your output variables (list_toFlowOpps)
//here my list = the list returned from the doTheThing method
theOutputs.list_toFlowOpps = dotheThing(
//I pass the inputs to the method
i.rec_toApexOpp,
i.list_toApexOpps
);
//for my next output, I can set its value directly here
theOutputs.int_toFlow = numberOfOpportunities;
results.add(theOutputs);
}
return results;
}
/*here I take the list of opportunities from the flow, loop through them and change the close date based on the Opportunity record from the flow.I do not update the records here, but send them back to the flow. */
public static List<Opportunity> doTheThing(
Opportunity rec_toApexOpp,
List<Opportunity> list_toApexOpps
) {
//use the variables to do something
List<Opportunity> list_toFlowOpps = new List<Opportunity>();
for (Opportunity loopOpp : list_toApexOpps) {
if (loopOpp.CloseDate < rec_toApexOpp.CloseDate) {
loopOpp.CloseDate = rec_toApexOpp.CloseDate;
}
list_toFlowOpps.add(loopOpp);
}
numberOfOpportunities = list_toFlowOpps.size();
return list_toFlowOpps;
}
//list each flowinput with @InvocableVariable above it
public class FlowInputs {
@InvocableVariable
public Opportunity rec_toApexOpp;
@InvocableVariable
public List<Opportunity> list_toApexOpps;
}
//list each flowOutput with @InvocableVariable above it
public class FlowOutputs {
@InvocableVariable
public List<Opportunity> list_toFlowOpps;
@InvocableVariable
public Integer int_toFlow;
}
}
I hope this is helpful to you! What other examples would you like to see?
One thought on “Invocable Methods Made Easier: A Better Way to Send Data Between Flow and Apex”