Introduction
According to Salesforce Help: An approval process automates how records are approved in Salesforce. It specifies each step of approval, including from whom to request approval and what to do at each point of the process.
Assumptions
- Position Custom Picklist field on the User object, with values: “Employee”, “Manager”, “Director”,
- Apex class that dynamically sets multiple approvers based on the Position value from the User object and submits Approval Process,
- 2-step Approval Process on Opportunity Object – for Manager and Director Approval, with manually chosen approver.

Problem
According to Salesforce Help, ProcessRequest class provides setNextApproverIds(ID[] nextApproverIds)
method. Unfortunately, the List passed to this method must have only one element.

If we pass more than one User Id to this method, we will receive an error message:
System.DmlException: Process failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, missing required field: [nextApproverIds]: [nextApproverIds]
Solution
The workaround is to create a Process Request for each Approver:
public static void initApprovalProcess(List<Id> sObjectIdList) {
List<Approval.ProcessSubmitRequest> approvalRequestList = new List<Approval.ProcessSubmitRequest>();
List<User> managerUserList = [
SELECT
Id
FROM
User
WHERE
Position__c = 'Manager'
];
for (Id sObjectId : sObjectIdList) {
for (User managerUser : managerUserList) {
//Class to submit a record for approval.
Approval.ProcessSubmitRequest approvalRequest = new Approval.ProcessSubmitRequest();
//Sets the comments to be added to the approval request
approvalRequest.setComments('Submitting request for approval.');
//Sets the ID of the record to be submitted for approval. For example, it can specify
// an account, contact, or custom object record.
approvalRequest.setObjectId(sObjectId);
//If the next step in your approval process is another Apex approval process, you specify
// exactly one user ID as the next approver. If not, you cannot specify a user ID and this
// method must be null
approvalRequest.setNextApproverIds(new List<Id>{managerUser.Id});
approvalRequestList.add(approvalRequest);
}
}
Approval.process(approvalRequestList);
}
After submitting the Approval Process, we will set next approvers and proceed to Manager Approval step:

To proceed to the next step, we need to get all Process Work Items and for each record, we will create a new Process Work Item for every step:
//Helper method get Process Instance Work Items which represents a user’s pending approval request.
private static List<ProcessInstanceWorkitem> getProcessInstanceWorkItems(Id objectId) {
return [
SELECT
Id
FROM
ProcessInstanceWorkitem
WHERE
ProcessInstance.TargetObjectId =: objectId
];
}
//Helper method to create new Process Work Item Request record to process an approval request after it is submitted.
private static Approval.ProcessWorkitemRequest createProcessWorkItemRequest(Id approvalStepId, Id approverId, String approvalAction, String commentFromApprover){
Approval.ProcessWorkitemRequest result = new Approval.ProcessWorkitemRequest();
result.setComments(commentFromApprover);
result.setAction(approvalAction);
result.setWorkitemId(approvalStepId);
if (approverId != null) {
result.setNextApproverIds(new List<Id>{approverId});
}
return result;
}
To Approve, Reject or Recall Manager step, we will use this method:
public static void approveRecordForManager(Id sObjectId, String action, String commentFromApprover) {
List<Approval.ProcessWorkitemRequest> approvalRequestList = new List<Approval.ProcessWorkitemRequest>();
if (action == 'Approve') {
List<User> directorUserList = [
SELECT
Id
FROM
User
WHERE
Position__c = 'Director'
];
for (ProcessInstanceWorkitem workItem : getProcessInstanceWorkItems(sObjectId)) {
for (User directorUser : directorUserList) {
approvalRequestList.add(createProcessWorkItemRequest(workItem.Id, directorUser.Id, action, commentFromApprover));
}
}
} else if (action == 'Reject' || action == 'Removed') {
for (ProcessInstanceWorkitem workItem : getProcessInstanceWorkItems(sObjectId)) {
approvalRequestList.add(createProcessWorkItemRequest(workItem.Id, null, action, commentFromApprover));
}
}
Approval.process(approvalRequestList);
}
After approving Manager Approval step, we will proceed to the Director Approval step:

As Director Approval is the last step, we won’t set next approvers and the method will be like this:
public static void approveRecordForDirector(Id sObjectId, String action, String comment) {
List<Approval.ProcessWorkitemRequest> approvalRequestList = new List<Approval.ProcessWorkitemRequest>();
for(ProcessInstanceWorkitem approvalProcessWorkingItem : getProcessInstanceWorkItems(sObjectId)) {
approvalRequestList.add(createProcessWorkitemRequest(approvalProcessWorkingItem.Id, null, action, comment));
}
Approval.process(approvalRequestList);
}
And the whole process will be approved:

Important Notes
When we look at the Approval History related list, we will see that there are duplicated Approval Steps and approvers.

This is because each Approver has a separate Process Work Item Request record. It’s necessary to dynamically assign multiple approvers. To adjust this to the standard-like view, we will need to implement a custom Lightning Component or Visualforce page.
To Recall the Approval step, the Action value will be “Removed”.
ApprovalProcessService.approveRecordForManager('0061r00001Co2MoAAJ', 'Removed', 'Recalled by Manager');
Resources
- https://help.salesforce.com/articleView?id=what_are_approvals.htm&type=5
- https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_ProcessRequest.htm
Hello!
I repeat these steps, but get System.DmlException: Process failed. First exception on row 0; first error: ALREADY_IN_PROCESS, This record is currently in an approval process. A record can be in only one approval process at a time.
What i am doing wrong?
Thanks!
It worked. Thanks.
For some reason it is not working for me. I have two step approval process where each level has multiple approvers. I am trying to approve one of the work items (Level 1) via apex but it gives me this error – “System.DmlException: Process failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, missing required field: [nextApproverIds]: [nextApproverIds]” x as it doesn’t allow me to proceed without nextApproverId. But I don’t want to trigger second Level (like Director) unless first Level (Manager) is fully approved. It works fine if I have single approver in level 1. What am I missing… Read more »
Hi Pawel
Another question – how have you designed the actually approval process? Does it have one or two approval steps? For some reason createProcessWorkItemRequest is not working for me to trigger the next level of approvers. It is automatically executing the final approval action once all level one approvers (like in you case – manager) approves the record.
Hi Pawel, how is the approveRecordForManager method called?
Hi..How is this method ‘approveRecordForManager’ triggered like how is it decided when to trigger then next level of approval?
Hi, I’m dealing with a similar requirement. Please verify – the above implementation would necessarily submit the approval to the multiple approvers at once, and they’ll be pending approval simultaneously and not sequentially. Is that correct?
hi Paweł Burzak m getting error in setnextapproverid ,this variable not accepting the list
When is the last time this was tested? I am using the code above, but I get an error: NO_Applicable_Process, No applicable approval process was found. I can create a single approval process in APEX successfully, but when I use the above code I get the error. I’m assuming that the error is because you can’t create multiple approval processes concurrently on the same object. I have seen other posts that suggest the same solution as above, but they are from 2013. I’m wondering if SF has made changes that make the above solution longer viable. Which is why I… Read more »