Skip to content

How to Call Invocable Method From Salesforce Flow?

How to Call Invocable Method from Salesforce Flow?

Invocable Method provides a very powerful way to use Apex functions not available out of the box in Salesforce Flow.

1. What are Invocable Actions?

Invocable actions give developers an opportunity to write any complex logic in Apex and expose it into Flow Builder.

2. What is an Invocable Method?

In order to call an Apex method from Flow you will have to annotate that method with @InvocableMethod.

This code sample shows an invocable method with the generic sObject data type.

public with sharing class GetFirstFromCollection {
  @InvocableMethod
  public static List <Results> execute (List<Requests> requestList) {
    List<SObject> inputCollection = requestList[0].inputCollection;
    SObject outputMember = inputCollection[0];

    //Create a Results object to hold the return values
    Results response = new Results();

    //Add the return values to the Results object
    response.outputMember = outputMember;

    //Wrap the Results object in a List container 
    //(an extra step added to allow this interface to also support bulkification)
    List<Results> responseWrapper= new List<Results>();
    responseWrapper.add(response);
    return responseWrapper;    
  }
}

public class Requests {
  @InvocableVariable(label='Records for Input' description='yourDescription' required=true)
  public List<SObject> inputCollection;
  }

public class Results {
  @InvocableVariable(label='Records for Output' description='yourDescription' required=true)
  public SObject outputMember;
  }
}

3. InvocableMethod Considerations

  • The invocable method must be static and public or global, and its class must be an outer class.
  • Only one method in a class can have the InvocableMethod annotation.
  • Other annotations can’t be used with the InvocableMethod annotation.
  • There can be at most one input parameter and its data type must be one of the following:
    • A list of a primitive data type or a list of lists of a primitive data type – the generic Object type is not supported.
    • A list of an sObject type or a list of lists of an sObject type.
    • A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).
    • A list of a user-defined type, containing variables of the supported types or user-defined Apex types, with the InvocableVariable annotation.
  • If the return type is not Null, the data type returned by the method must be one of the following:
    • A list of a primitive data type or a list of lists of a primitive data type – the generic Object type is not supported.
    • A list of an sObject type or a list of lists of an sObject type.
    • A list of the generic sObject type (List<sObject>) or a list of lists of the generic sObject type (List<List<sObject>>).
    • A list of a user-defined type, containing variables of the supported types or user-defined Apex types, with the InvocableVariable annotation.
Business AnalystSalesforce Certified Strategy Designer Badge LogoSalesforce Certified User Experience Designer BadgeSalesforce Certified Associate BadgeSalesforce Certified Administrator LogoSalesforce Certified Sales Representative Badge
Popular Salesforce Certification Guides

4. Invocable Method to Approve, Reject & Recall Records in an Approval Process Using Salesforce Flow

This code sample shows an Invocable Method to Approve, Reject & Recall records in an Approval Process

public without sharing class DYDCApprovalProcess {
    
    @InvocableMethod(label='Approval Process Action' description='Invocable Method to Process Approval Requests')
    public static List<Results> processApprovalRequestAction(List<Requests> requests) {
        List<Results> responseWrapper = new List<Results>();
        Map<Id, Id> processInstanceWorkItemMap = getProcessInstanceIdfromRecordId(requests);
        for (Requests curRequest : requests) {
            Results response = new Results();
            Approval.ProcessWorkitemRequest approvalWorkItem = new Approval.ProcessWorkitemRequest();
            approvalWorkItem.setComments(curRequest.comments);
            approvalWorkItem.setAction(curRequest.action);
            if(curRequest.approvalRecordId != null && processInstanceWorkItemMap.get(curRequest.approvalRecordId) != null)
            {
                approvalWorkItem.setWorkitemId(processInstanceWorkItemMap.get(curRequest.approvalRecordId));
            } 
            
            // Submit the request for approval
            Approval.ProcessResult approvalResult =  Approval.process(approvalWorkItem);

            response.isSuccess = approvalResult.isSuccess();
            Database.Error[] errors = approvalResult.getErrors();
            response.errorString = getErrorInfo(errors); 
            response.currentApprovalProcessStatus = approvalResult.getInstanceStatus();

            //Wrap the Results object in a List container
            responseWrapper.add(response);
        }
        return responseWrapper;

    }

    public static String getErrorInfo(Database.Error[] errors) {
        String errorStrings = '';
        if (errors != null) {
            for(Database.Error error : errors) {
                errorStrings = errorStrings + ' ' + error.getMessage();
            }
        }
        return errorStrings;
    }

    public static Map<Id,Id> getProcessInstanceIdfromRecordId (List<Requests> requests){
        Set<Id> recordSet = new Set<Id>();
        Set<Id> processInstanceIdSet = new Set<Id>();
        
        Map<Id, Id> processInstanceMap = new Map<Id,Id>();
        Map<Id, Id> processInstanceWorkItemMap = new Map<Id,Id>(); 
 
        for (Requests curRequest : requests) {
            recordSet.add(curRequest.approvalRecordId);
        }
        
        for (ProcessInstanceWorkitem invsWorkItem : [select Id, ProcessInstance.TargetObjectId from ProcessInstanceWorkitem 
                                                        where ProcessInstance.TargetObjectId in : recordSet])        
            {
                processInstanceWorkItemMap.put(invsWorkItem.ProcessInstance.TargetObjectId, invsWorkItem.Id);
            }            
        return processInstanceWorkItemMap;    
    }    

    public class InvocableErrorException extends Exception {
    }


    public class Requests {

        @InvocableVariable
        public String approvalRecordId;

        @InvocableVariable
        public String comments;

        @InvocableVariable
        public String action; // 'Approve', 'Reject'. 'Removed'
    
        @InvocableVariable
        public List<String> nextApproverIds;
    }

    public class Results {

        @InvocableVariable
        public Boolean isSuccess;

        @InvocableVariable
        public String errorString;

        @InvocableVariable
        public String currentApprovalProcessStatus;  //Approved, Rejected, Removed or Pending.

    }
}

The above Invocable Method will be available as a Flow Action.

New Salesforce Flow Action

You can create a screen flow to use this action.

Call Invocable Method as Flow Action

You can use the flow as a Quick Action to be called on click of a Button.

Call Salesforce Flow as Quick Action

5. Additional Resources


Recommend Articles

Share this article...

Please Leave a Comment

error: Content is protected !!

Discover more from DYDC

Subscribe now to keep reading and get access to the full archive.

Continue reading