Andy in the Cloud

From BBC Basic to Force.com and beyond…

Calling Flow from Apex

46 Comments

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.

ReturnHelloWorldFlow

ReturnHelloWorldAssignment

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.

CalcFlow CalcFlowAssignment

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.

RecordUpdaterFlowRecordUpdaterAssignment

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.

46 thoughts on “Calling Flow from Apex

  1. 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

    • 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.

  2. 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?

  3. 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!

    • 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? 😉

  4. 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.

  5. This works really well as an alternative to creating a rules engine in apex.

  6. Great post Andy!! You’ve single-handedly made me start dabbling in this at work!

  7. Pingback: Stumbled on another AWESOME Apex blog! - Salesforce coding lessons for the 99%

  8. 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

  9. 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!

    • 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.

      • 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.

      • Welcome

  10. 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.

  11. 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.

  12. 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.

  13. 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

    • 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.

  14. SF doesn’t let me move the flow interview instance into the execute method. It gives me an error about looking for ; and found {

  15. 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();
    }
    }

  16. Pingback: Tip# 6 : Calling a flow from APEX | CRM Crazy - Salesforce

  17. Pingback: Supercharging Salesforce Report Subscriptions with Apex and Flow | Andy in the Cloud

  18. 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.

    • Can you maybe share a Gist of your code? Also double check your flow name.

      • 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].

      • 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

      • ignore the pass list to flow comment… we have not gotten that far…

      • Not sure what you mean?

  19. Pingback: Dynamically Creating Flows in Apex | Andy in the Cloud

  20. Pingback: Introducing the Flow Factory | Andy in the Cloud

  21. Can we invoke a flow from @InvocableMethod

    • 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?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s