Since Summer’14 it has been possible to invoke a Flow from Apex, through the oddly named Interview system class method start. This blog talks about using this as a means to provide an extensibility mechanism, as an alternative option to asking an Apex developer to implement an Apex interface you’ve exposed. Clicks not code plugins!
Prior to Summer’14 it was only possible to embed a Flow in a Visualforce page, using the the flow:interview component, as described here. But this of course required a UI and hence user interaction to drive it. What if you wanted to allow admins to extend or customise the flow of some Apex logic using Flow from a none UI context?
Enter trigger ready-flows, first introduced as part of the Summer’14 pilot for running Flow from Workflow, which itself is still in Pilot, thought the ability to create trigger ready-flows is now generally available, yipee! Here’s what the docs have to say about them…
You can also build trigger-ready flows. A trigger-ready flow is a flow that can be launched without user interaction, such as from a flow trigger workflow action or the Apex interview.start method. Because trigger-ready flows must be able to run in bulk and without user interaction, they can’t contain steps, screens, choices, or dynamic choices in the active flow version—or the latest flow version, if there’s no active version.
This blog will present the following three examples of calling trigger-ready flows from Apex.
- Hello World, returning a text value set in a Flow
- Calc, shows passing in values to a Flow and returning the result of some processing.
- Record Updater, shows passing in some SObject records to the Flow for processing and returning them.
Hello World Example
Here is a simple example that invokes a Flow that just returns a string value defined within the Flow.
The following Apex code calls the above Flow and outputs to the Debug log.
// Call the Flow Map<String, Object> params = new Map<String, Object>(); Flow.Interview.ReturnHelloWorld helloWorldFlow = new Flow.Interview.ReturnHelloWorld(params); helloWorldFlow.start(); // Obtain the results String returnValue = (String) helloWorldFlow.getVariableValue('ReturnValue'); System.debug('Flow returned ' + returnValue);
This outputs the following to the Debug log…
11:18:02.684 (684085568)|USER_DEBUG|[13]|DEBUG|Flow returned Hello from the Flow World!
Calc Example
This example passes in a value, which is manipulated by the Flow and returned.
The following code invokes this Flow, by passing the X and Y values in through the Map.
// Call the Flow Map<String, Object> params = new Map<String, Object>(); params.put('X', 10); params.put('Y', 5); Flow.Interview.Calc calcFlow = new Flow.Interview.Calc(params); calcFlow.start(); // Obtain the results Double returnValue = (Double) calcFlow.getVariableValue('ReturnValue'); System.debug('Flow returned ' + returnValue);
This outputs the following to the Debug log…
12:09:55.190 (190275787)|USER_DEBUG|[24]|DEBUG|Flow returned 15.0
Record Updater Example
With the new SObject and SObject Collection types in Summer’14 we can also pass in SObject’s. For example to apply some custom defaulting logic before the records are processed and inserted by the Apex logic. The following simple Flow loops over the records passed in and sets the Description field.
This code constructs a list of Accounts, passes them to the Flow and retrieves the updated list after.
// List of records List<Account> accounts = new List<Account>{ new Account(Name = 'Account A'), new Account(Name = 'Account B') }; // Call the Flow Map<String, Object> params = new Map<String, Object>(); params.put('Accounts', accounts); Flow.Interview.RecordUpdater recordUpdaterFlow = new Flow.Interview.RecordUpdater(params); recordUpdaterFlow.start(); // Obtain results List<Account> updatedAccounts = (List<Account>) recordUpdaterFlow.getVariableValue('Accounts'); for(Account account : updatedAccounts) System.debug(account.Name + ' ' + account.Description);
The follow debug update shows the field values set by the Apex code and those by the Flow…
13:10:31.060 (60546122)|USER_DEBUG|[39]|DEBUG|Account A Description set by Flow 13:10:31.060 (60588163)|USER_DEBUG|[39]|DEBUG|Account B Description set by Flow
Summary
Calling Flows from Apex is quite powerful to provide more complex extensibility back into the hands of admins vs developers. Though depending on your type of solution is somewhat hampered by the lack of the ability to dynamically create a subscriber configured Flow. As such for now is really only useful for none packaged Apex code deployments in production environment, where the referenced Flow’s can be edited (managed packaged Flows cannot be edited).
Despite this, for custom Apex code deployments to production it is quite powerful as it allows tweaks to the behaviour of solutions to be made without needing a developer to go through Sandbox and redeployment etc. Of course your also putting a lot of confidence in the person defining the Flows as well, so use this combo wisely with your customers!
Upvote: I’ve raised an Idea here to allow Apex to Dynamically create Flow Interview instances, with this in place ISV and packaged applications can make more use of this facility to provide clicks not code extensibility to their packaged application logic.
Update: Blog post Flow Factory presents a workaround to the dynamic calling problem.
October 26, 2014 at 5:19 pm
This is good example .I had one for how to use this flows to connect to external system.Would like to know your feedback on this http://cloudyworlds.blogspot.in/2014/09/invoking-apex-callouts-through-visual.html
October 26, 2014 at 6:32 pm
Thats looks great, i love the Apex Plugin feature of Flow, sadly, though understandably enough for now, ‘trigger ready-flows’ cannot contain Apex Plugin Elements. So effectively you cannot call a Flow from Apex, that then calls back out to Apex via an Apex Plugin. It’s a bit of strange use case i admit, hopefully they will add it in the future, it seems a shame to block Flows using Flow Plugins.
October 27, 2014 at 10:03 am
Hi Andy,
This was a helpful article for me as I was trying to use flows in VF Page and there are some choices on the Flow, and trying to select the Choice via code as I have two choices on the screen “Yes” and “No” but could not succeeded, can you please suggest that Is this possible to select Choice of a screen via code?
October 28, 2014 at 2:05 pm
This won’t help with that i’m afraid, that is more to do with the logic in your Flow than how you invoke it. I’ve not studied choices in detail, but i imagine there is a means to highlight a selected one, have you reviewed the Flow Implementation guide section on choices? https://eu3.salesforce.com/help/pdfs/en/salesforce_vpm_implementation_guide.pdf
October 29, 2014 at 11:33 am
Thanks Andy, I checked this guide but did not found any solution to select any choice or set value of dropdown list
October 27, 2014 at 1:58 pm
It is a really cool idea, and I could see how there could be some powerful use cases, but I am not 100% on board with giving so much power over to the declarative side of things where testing is very lacking and sometimes actions aren’t 100% thought out. I am not at all against giving admins more control, I just don’t like the idea that an untrained individual can make changes like this directly in prod (assuming a trained admin would know better and make changes in a sandbox first for testing). This could instantly break some functionality and the process for reverting it would be all manual at that point. Still, it is probably more a matter of training than anything else.
Good read!
October 28, 2014 at 2:03 pm
Yeah totally agree and its a balance Salesforce are going to have to address, the more and more they head into Declarative things like Lightning Process builder. Maybe an idea to have LIghtning Test Harness? 😉
November 5, 2014 at 3:19 pm
I recently added code like this to my instance. At first, I was just returning a page reference by the flow url. Then I changed calling the Flow from apex directly using the the same method as this post.
You might want to be aware of this known issue.
https://success.salesforce.com/issues_view?id=a1p300000008XAoAAM
This was preventing me from deploying the code to production because of “Internal Error” in one test class. The workaround does not seem to make any sense, the flow in question does not even have any task assignments, but for some reason disabling the “Enable User Control over Task Assignment Notifications” allowed me to deploy without error.
November 8, 2014 at 11:14 am
Thanks for sharing this news Justin, appreciated!
November 11, 2014 at 10:53 am
Reblogged this on SutoCom Solutions.
December 17, 2014 at 7:28 pm
This works really well as an alternative to creating a rules engine in apex.
December 18, 2014 at 8:26 am
Thanks Tom!
January 16, 2015 at 10:20 pm
Great post Andy!! You’ve single-handedly made me start dabbling in this at work!
January 18, 2015 at 10:48 am
Excellent, its an awesome tool and key to the platforms future in my view.
Pingback: Stumbled on another AWESOME Apex blog! - Salesforce coding lessons for the 99%
February 17, 2015 at 11:05 am
Hi Andrew,
I’m trying to pass list of records from Apex into a Flow using @Invocable
method. However In Flow I could see Output as only the 1st record from
list. Is there a way wherein we can display all the records?
Basically in Flow I have a page where a User can select Multiple Check
boxes for Example lets take Months ( January to December). Once we select
the Months from the Page, the selection goes to Apex Class where I have
@Invocable method and then it passes back the Ids of the Months I selected
in the Flow. In Flow I have SObject variable which I associate as Output to
display the List, but I end up getting only the 1st record from the List
and not the whole List. Is there a way where in I can display the whole
List.
Thanks,
Anup
February 17, 2015 at 1:25 pm
Are you able to share a Gist snippet with the code your using and a screenshot of your Flow configuration?
April 15, 2015 at 4:12 pm
This is some great information. I’ve built a trigger which instantiates the flow, passes in the parameters, then call the start method for the flow instance.
I modify data to execute the trigger and I don’t receive any error. I open the debug log and find the parameters passed in are correct and the flow start method enters and exits without any issue. The problem is that the flow doesn’t appear to be running. I’m not seeing the record update.
Is there anything that needs to be added besides the flow start method? I see you used the getVariableValue method as well, but I’m assuming that’s if you want to manipulate the data further. Our flow runs a fast update at the end, so no need to run any DML or further manipulation through code.
My other big question, which I’m trying to test, is how do triggered flows work with governor limits? From what I’m seeing our current demo is not bulkified; we trigger the flow within a for loop and the flow runs a fast lookup (I would assume is running SOQL) and runs a fast update (I would assume runs a DML). Would we get errors and need to bulkify by adjusting the parameters the flow is looking for?
By the way – thanks for being a great resource to the Salesforce community!
April 15, 2015 at 4:39 pm
Thank you for your kind words, i really enjoy contributing to the community. Regarding debugging Flow, take a look at this and see if this helps, http://docs.releasenotes.salesforce.com/he-il/spring14/release-notes/rn_forcecom_process_debug_logs_flow_action.htm. Regarding bulkification yes, you do need to consider this, in my tests in the past i can see that fast lookup and fast update are akin to SOQL and DML, so for sure do not put Flow calls in a loop. You should be able to leverage Flow Collection variables to pass in lists and then ensure your Flow is bulkified, though i have not personally tried this. Hope this helps! Let me know how you get on.
April 15, 2015 at 9:21 pm
Thanks for the link. What I’m finding is it’s nearly impossible to retrieve data from a parent record then iterate through the child records and update a field with the retrieved data because flow doesn’t have any functionality similar to maps. It seems Salesforce would need to support a collection that has a key for apex triggered flows to be a practical solution for update parent or child records with data from the related record. Still a really cool and powerful concept.
I ended up playing around with the limits as well. I built a lookup within a loop within a lookup within a loop (intended to break it) and at first it worked – I updated about 250 records this way, which I thought was odd. When I scaled up to update 4000 record I hit the limit and it rolled everything back.
Thanks again for the help.
April 16, 2015 at 12:28 pm
Welcome
April 16, 2015 at 2:32 pm
Is there any way to call a flow in a scheduled Apex class? If it is possible, and you are nice enough to explain how, can you please include the whole class declaration?
I am a noob to APEX and am trying to get the flow.interview to be called through a schedulable apex class. The ultimate goal is to send emails to users who haven’t logged the last 90 days and warn them they might lose their license. I have the two flows working, I just need to call them both once a night.
April 16, 2015 at 3:22 pm
I believe there is a schedule element in flow now, have you seen it?
April 16, 2015 at 3:37 pm
Nope. But if there is such an element, that would be great. I went looking for a scheduling element and didn’t find anything. The only thing close is the wait element. If you could point me in the right direction either way, I’d be grateful.
April 16, 2015 at 5:38 pm
Yeah that’s it, I myself have yet to look into it, but worth a look I would say!
April 17, 2015 at 8:56 am
The wait element is more for time based actions within the flow. It doesn’t help trigger the flow itself. If only SF had the possibility to fire off workflow on a login. Then my problem would be solved. If you can help me with the Apex class on “Schedulable”, I’d really appreciate it.
April 17, 2015 at 9:16 am
Ok. I have this now and SF swallows it.
global class ScheduledLicenseExpirationReminder implements Schedulable
{
public Flow.Interview.Remind_Users_About_Expiring_Licenses userReminder {get; set;}
global void execute(SchedulableContext SC) {
userReminder.start();
}
}
What do you think?
Scott
April 17, 2015 at 2:30 pm
Try making it “public final”, Saleforce maybe loosing the variable value and giving you a null pointer exception, which should show under Apex Jobs page under Setup btw. Otherwise trying getting hold of the flow interview instance in the execute method instead of depending on it being setup outside the class.
April 17, 2015 at 3:13 pm
SF doesn’t let me move the flow interview instance into the execute method. It gives me an error about looking for ; and found {
April 17, 2015 at 5:20 pm
Paste your code here and I will try and look
July 29, 2015 at 2:24 pm
Any chance you could give me an example where you call the flow as schedulable AND you don’t pass anything into the flow? I just need to be able to call my flow on a Monthly basis via scheduled APEX. I don’t need to pass anything to the flow as the flow is just doing some maintenance items for me.
Here is my attempt:
global class scheduled_BadgeReset implements Schedulable {
global void execute(SchedulableContext SC) {
Flow.Interview.Reset_Badge_Count_Each_Month resetBadge = new Flow.Interview.Reset_Badge_Count_Each_Month();
resetBadge.start();
}
}
July 29, 2015 at 9:41 pm
What’s the problem?
Pingback: Tip# 6 : Calling a flow from APEX | CRM Crazy - Salesforce
Pingback: Supercharging Salesforce Report Subscriptions with Apex and Flow | Andy in the Cloud
December 3, 2015 at 12:44 pm
Have you had this error occur? Constructor not defined: [Flow.Interview.Update_Account_Team_with_DSM_when_no_AC].<Constructor>(List<Account>)
I followed the steps described to initiate the flow. I am wondering if I am missing something in my scheduled apex code that was not shown in your example.
December 3, 2015 at 12:46 pm
Can you maybe share a Gist of your code? Also double check your flow name.
December 8, 2015 at 10:14 pm
global class scheduleUpdateAccountTeamwithDSM implements Schedulable{
global void execute(SchedulableContext ctx)
{
startFlow();
}
public void startFlow()
{
// get accounts with no AC
List params = new List([Select Id from Account where Id NOT IN (select AccountId From AccountTeamMember WHERE TeamMemberRole = ‘Advertising Consultant – ATC’)]);
// pass list to flow
Flow.Interview.Update_Account_Team_with_DSM_when_no_AC_headless uatDSM = new Flow.Interview.Update_Account_Team_with_DSM_when_no_AC_headless();
system.debug(‘FLOW STARTING:Update_Account_Team_with_DSM_when_no_AC_headless’);
uatDSM.start();
}
}
Constructor not defined: [Flow.Interview.Update_Account_Team_with_DSM_when_no_AC_headless].
December 8, 2015 at 10:17 pm
You need to pass parameter into the constructor. Check the apex developers guide or my sample code it passes a map of strong and object in as a parameter
December 8, 2015 at 10:15 pm
ignore the pass list to flow comment… we have not gotten that far…
December 8, 2015 at 11:03 pm
Not sure what you mean?
Pingback: Dynamically Creating Flows in Apex | Andy in the Cloud
March 17, 2016 at 11:46 am
Idea upvoted!
March 17, 2016 at 7:54 pm
Thank you!
Pingback: Introducing the Flow Factory | Andy in the Cloud
October 26, 2016 at 11:50 am
Can we invoke a flow from @InvocableMethod
October 27, 2016 at 9:41 am
I have not tried it and don’t know if any restrictions so try it out. Let me know. Also interested in your use case? Since Flow can call another Flow natively already? Conditional execution I am guessing?
February 12, 2018 at 2:38 pm
Hi, Andy, I have a method that invokes a flow that updates records. Now we are trying to create unit tests around this but cannot assert the values set by the flow later in the unit test. Have you done anything like this? Is it possible to assert object values after updated by a flow in a unit test?
February 12, 2018 at 6:58 pm
Hmmm that is odd. Have you tried wrapping your Flow invocation in Test.start() and Test.finish() method calls?