Flow Action: Call Subflow

Hi there! 🙋🏻‍♂️

Are you confused why we are talking about an Apex action that call another flow when we already have a ‘Subflow’ element in flows? Well, allow me…

If you’ve already started playing around with Record Triggered or Platform Event triggered flows, you might have noticed that the Subflow element is missing for them. Yes, so for the time being, we cannot call subflows in the above specified flow types. Quite a bummer!

Not to worry though! Apex actions to our rescue again.

How does it work?

So I’ve built an Apex action that enables you to call a flow as a ‘subflow’.
The action takes follow values as input/output:

ParameterUse for InputUse for OutputDescription
Flow API NameAPI name of the flow.
Flow NamespaceNamespace of the flow. Leave the input blank if the flow does not have a namespace.
JSON of Input VariablesJSON string of flow input variables and their corresponding values.
Ex: If your flow has two input variables; varText(text type) and varNumber(number type), then the JSON should look like this.
“varText” : “Text Input”,
“varNumber” : 3000
Store the JSON string in a variable using a Text type variable or a Text Template(with View as Plain Text) and pass that variable in the action input.

This action can prove to be very handy for calling for error handling flows and for many more use cases.

You can find the link for the unmanaged package and source code here:

Quick Demo? Of course!

Hope you found this one useful! Catch you in the next one! ✌
Thank you for being an awesome reader! Subscribe to this blog for receiving all the latest updates straight to your inbox. 🙂

60 thoughts on “Flow Action: Call Subflow

  1. Pingback: Winter ’21 – Introducing the Before Delete Record-Triggered Flow – Jenwlee's Salesforce Blog

  2. Pingback: Winter ’21 – Introducing the Before Delete Record-Triggered Flow – Jenwlee's Salesforce Blog - Salesforce News & Tutorials

  3. Thanks for the work and the solution! Quick questios, does the Actoiun allow objects to be sent? For example, could I send the following to a subflow:


    Thanks for your help!


    • I haven’t tried it. Ideally speaking it should, but I’ve my doubts.

      Alternate would be to pass the record Id and query the record again in subflow, but that costs you an extra query unfortunately.


      • Well, I attempted it. I might be doing it wrong, but I did it exactly as above, and then also tried it without quotes (which didn’t work at all). Got this error when I sent with quotes:

        “Error Occurred: An Apex error occurred: System.FlowException: Unable to set value for variable ‘varAccount’. Flow encountered an error when processing and converting between data types. Please check the flow and ensure all data types are matched correctly.”

        The Apex Action did call the subflow, but nothing got passed over. I’ll try it with just the recordId and see what we can see. Thanks again for your work.


      • Can you try by removing the double quotes around the {!Record} variable?
        So something like:
        … : {!Record}

        Enclosing the variable in the double quotes makes it string(text) type variable. By removing the quotes we are passing the record as an Object.

        I’m not too hopeful but it’s worth trying. 🙂


  4. Here is the error:
    Error element call_sub_flow (FlowActionCall).
    An Apex error occurred: System.JSONException: Invalid numeric value: Leading zeroes not allowed at [line:2, column:15]

    Here is the point of the erro (xed out the ID after the 001):
    flowApiName = updatetest
    inputVariablesJSON = {!textJSON} ({ “varAccount”:Account (001xxxxxxxxxxxxxxx) })

    It worked perfectly when passing in the recordId. Still, this really helps me out. This way I don’t have to refactor an existing set of sub flows in my automation. I’d hate to reinvent the wheel.


    • Yeah, I can understand when you’ve to spend time doing “not so productive” stuff.

      But I guess for now, you’ll have to manage with passing the record Id. Because passing the record doesn’t seem to work.

      I’ve an idea but it’d require you to create another Invocable method(Apex action).

      JFYI, the idea would be to get serialized output of record from the Apex action and pass that in the varAccount.
      I’ve a feeling that it might work.
      But if that also doesn’t work then I don’t know what will.😅


  5. Is there a way to add a carriage return to the variables. I am trying to set a case description and based upon what was updated on the account the description can be one line or multiple paragraphs.

    What I have so far:
    “varCaseDescription” : “The name was changed from {!$Record__Prior.Name} to {!$Record.Name}

    What I want – 4 separate lines so that it is easier to read on the case.
    “varCaseDescription” : “The name was changed from


    • Hi Justin,

      Have you tried using \n for line breaks?

      “varCaseDescription” : “The name was changed from

      should look like:
      “varCaseDescription” : “The name was changed from \n{!$Record__Prior.Name} \nto \n{!$Record.Name}”


  6. Hello, is there a way to pass a null or blank field text?

    “input_text” : {!input_reemplaza}

    If {!input_reemplaza} is a null field, i get an error.


  7. Hello,

    I’m new to apex so I’m a little lost. I’m trying to use this to call a flow we created that is a 1 minute pause. So we don’t need to pass any info int the “Sub Flow”. Only need it to trigger the other flow. We are using this pause in a loop to wait for a field to be set then it will break the loop.

    I filled in the Flow API Name in the action but I’m not sure what to write into the JSON section. Any help would be great.

    Thank you


      • Hi ForcePanda,

        Thanks for the reply. The scenarios I tested and the results below. This error is triggered when I go to save a file that triggers the record triggered flow I created.

        If I don’t include using the JSON parameter I get this error:
        Error Occurred: An Apex error occurred: System.NullPointerException: null input to JSON parser

        If I include the JSON parameter and use the Null value in the variable I get this error:
        An Apex error occurred: System.JSONException: no content to map to Object due to end of input

        If I include the JSON parameter and use the blank value in the variable I get this error:
        Error Occurred: An Apex error occurred: System.JSONException: no content to map to Object due to end of input


      • Yes that did work! Thank you very much!

        Now I have a new problem though haha. I am trying to use a sub flow to trigger an auto launched flow that has a pause in it. The flow I’m calling it from is a record triggered flow and I was hoping I could use that pause but it just fires that flow and keeps going through the logic… silly me. Do you know how to use a pause function inside of a record triggered flow?


      • So we ran into an issue recently where some emails were being sent out blank because one of the fields it is referencing was not set. There is a process outside of SFDC that sets that field and sometimes it’s delayed and sets the field after the email is triggered. My idea is the put the current logic into a flow and wait until that field is set and then send the emails. So currently the flow can take 2 paths. One is where that field is set and it looks at a subscription name to determine what email to send. The other path is where the one field is not set and it goes through some loop type logic that checks if that field is set over and over for about 10 minutes every X amount of minutes. It will either trigger an error email after 10 minutes of it not being set or it gets set during that time and continues on path 1. My idea was to use a sub flow to pause and then continue the current flow once that pause is complete. That is not the case and it just triggers that flow and moves on to the next action in the logic.


      • No actually, I have never used that before so I didn’t think to try it. It looks pretty straight forward. I assume I can connect all the logic to the scheduled path and set the schedule to a 10 minuet delay?


      • Unfortunately, you can only do hours and days.

        One solution that I can think of is using a scheduled path to fire it after 0 hours, and then call subflow which has a pause element for 10 mins.


      • Ok. I’m unsure how to actually pause the flow though with the subflow. The way it’s setup now is that it does call the sub flow for the pause but it’s then continuing to the nest step before the actual pause is over. I would like it to complete the pause sub flow before it continues to the next piece of logic in the flow


      • I sure would but I’m not sure how. I don’t see an attachment button and I’ve also tried copy and pasting but to avail.


      • We only want this to run when the record is created. So if that field is set after this record is created we are in the same boat as before.

        I may be overlooking what you explaining though. From what I gather the logic you are presenting is when a record is created and that field is set.


      • As you said, the field gets set by an external system.

        So do you mean you want to send the email only the first time when that field is set and not when it’s updated at some point later?


      • I only want to send the email when the DB Type field is set/not null and certain subscription rate plans are created. So yes, the email should only be sent once at the first time that Subscription is sold to them.


      • In that case, I think a more simpler approach here would be to create a checkbox to keep track of if the email is sent or not.

        Then create a flow which fires when the DB Type is updated as not null and checkbox is false(along with other conditions). In the flow, send the email and update the checkbox.


  8. This is just what I needed! Thank you! Do you have an example of how an output variable in the subflow can be passed back into the main Flow from the Call Subflow action? It looks like you allude to this a couple of times, but I’m not sure how to set it up. I tried adding the output variable in the text template for the JSON parameter in the Apex Action, but this didn’t seem to work (the first variable is an input parameter for my subflow, and the second one is the output parameter). Here is my text template:

    “RecordType_Id” : “{!$Record.RecordTypeId}”,
    “RecordType_ApiName” : “{!RecordType_ApiName}”

    My subflow is pretty simple — it’s just retrieving the RecordType Developer Name corresponding with the RecordType Id from the main Flow. The subflow is running in system context without sharing, so that the main Flow can run in user context with sharing (and so that I can avoid hard coding record type IDs in my Flows).

    Thanks in advance!


    • Output variable handling is a bit complicated to implement. What makes it complicated is there is no good way to make it generic. I’ve some ideas but can’t say anything until tried.

      The current component doesn’t handle output variables at all. So it’s no good if you want to use it to get some output from the subflow.


  9. I’m trying to send a list of Id’s through and it isn’t working for me. I’m not getting any error when debugging though and the subflow does not get called.
    My text template for the JSON is this:
    { “InOppIds” : [“{!$Record.Id}”] }
    And it turns into valid JSON:
    { “InOppIds” : [“0062h00000PLr7JAAT”] }


    • I updated it to try with a single Id and I’m getting the same result. Anything I could be missing?

      Changed JSON to:
      { “InOppId” : “0062h00000PLr7JAAT” }


      • It is marked as available for input. I have a fault path set up on the subflow action and the flow isn’t going down that path. I’m logging as soon as the subflow begins as well and it isn’t logging anything there. So I’m not sure what else to do.


      • Here is the body of my text template.

        { “InOppId” : “{!$Record.Id}” }

        When I print it out it looks right.
        { “InOppId” : “0062h00000PLr7JAAT” }

        Here is the error thrown running the flow
        An Apex error occurred: System.FlowException: An unhandled fault has occurred in this flow An unhandled fault has occurred while processing the flow. Please contact your system administrator for more information.

        And in the debug log here is the real error is…
        It’s actually one of my debug logs causing an error. So I’m all good. I don’t know what update fixed it but it’s working now.


  10. We were having some issues with the JSON variable for recordid not being passed into the subflows – this was caused by the sublows’ API being in older versions. Just a tip in case anyone encountered the same issue. 🙂


  11. Getting the following error when I use {} as input
    An Apex error occurred: System.JSONException: Unexpected character (‘


  12. Hey Forcepanda,

    Love the concept but one has to watch out for Limits! When this runs on a single record save, it works great. However, when one is trying to insert or update say 200 records, it bombs out if the sub-flow being invoked has a Get Records or Create, Update, or Delete element in it. That’s because the code instantiates each sublow and then runs the sub-flow for each record one after the other. This effectively calls the sub-flows serially and isn’t bulkified.

    This is caused by the Salesforce Interview apex class not offering a bulk way to start records. I was recently looking at Douglas Ayer’s “Mass Action Scheduler” and it bulk starts flows by calling the Salesforce Action’s API. It would be nice to have that capability too here but suspect you’d run into the callouts aren’t allow after DML issue.

    Have you run into this before and resolved it?


    • Yeah, it’s a big issue. Since Apex currently doesn’t support launching Flow interviews in bulk. There should be an idea for this on community.

      There is no nice/clean way to do it, AFAIK.

      Can you elaborate on your use case? I might be able to suggest something.


      • It’s actually a continuation of my issues above. I was trying to pass an array of Id’s in, but Flow Triggers only get 1 record at a time. Since I can’t pass the whole record from the trigger, I’m passing the Id only. Then pausing and doing 1 query, 1 assignment, 1 update, and I’m hitting 101 SOQL query limits.


      • Jake, the action might not allow you to pass a whole record but you can pass specific values that you want to use in your subflow instead of doing a query.


      • The only solution I can think of is to inline the sub-flow into the “master / calling” which defeats the purpose for reuse / maintenance. One could also use Apex instead but that requires developer skills.


      • Yeah, it’s best to decrease the batch size if you’re using this action in your record triggered flows.

        Just outta curiosity, are you doing this in a after save flow or before save flow?


      • The Flow record-triggered batch size is 200 records. If 600 records are updated, 3 batches of 200 record-triggered flow interviews run one after the other. Unfortunately, that can’t be controlled. One would have to control the batch size on whatever is inserting, updating, or deleting records en mass.

        An after-save flow was used because an Apex action can’t be invoked from a before-save flow.


      • Yes, what I meant was to decrease the batch size if you’re doing a mass update/insert.

        P.S. You should simply use PB instead of After Save flow as there is not any significant performance difference. PBs under the hood are just flows.

        It’s before save flows that are performant, understandably so because they don’t do an explicit DML.


  13. Pingback: Salesforce Sub-Flow Declarative Invocation Alternatives

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 )

Google photo

You are commenting using your Google 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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.