Programmatically Approving/Rejecting Flow Orchestration Approval Steps (and why ConnectApi.FlowApprovalProcesses won't work)
The problem
If you're using Flow Orchestration with Approval Steps and need to let users approve or reject work items from somewhere other than the standard Work Guide component — for example, from a custom mobile experience, since Work Guide does not render in the Salesforce mobile app despite showing up fine in Lightning App Builder's phone preview — you'll hit a wall pretty fast.
Here's what doesn't work, and why, so you don't have to burn the hours we did finding out:
Dead end #1: ConnectApi.FlowApprovalProcesses.getFlowApprovalProcessWithStatus()
This looks like exactly the right API — it's documented specifically for retrieving the status and available actions of a flow approval process. In practice, for Orchestration-embedded Approval Steps specifically (as opposed to standalone autolaunched Flow Approval Processes invoked via a "Request an Approval" flow element), we found:
- Calling it via Apex throws ConnectApi.ConnectApiException: Id=null when passed the Flow API name for processNames
- Calling the identical request via raw REST (same org, same inputs) succeeds and returns correct data — ruling out an Apex-only bug
- When it does succeed, it consistently returns isApprovalInProgress: true but only exposes a "Recall" action — never Approve/Reject, even when querying as the actual assigned approver, not the submitter
We filed this with Salesforce Support and it's now a tracked Known Issue: W-23702762. Support confirmed this API path does not currently support returning Approve/Reject actions for Orchestration Approval Steps.
Dead end #2: Classic Approval.process()
The older, well-documented Approval.ProcessWorkitemRequest / Approval.process() Apex API only works against classic ProcessInstanceWorkitem records. Flow Orchestration Approval Steps create ApprovalWorkItem records instead — a different object entirely. Attempting to use the classic API against an ApprovalWorkItem Id throws INVALID_CROSS_REFERENCE_KEY.
The actual solution: reviewApprovalWorkItem
There's a standard invocable action — not prominently linked from the API you'd naturally start with — that does exactly what's needed:
- Action name: reviewApprovalWorkItem
- Inputs: approvalWorkItemId (String), approvalDecision (String — must be exactly "Approve" or "Reject", capitalized), comments (String, optional)
- Preconditions: the work item's Status must be Assigned, and the calling user must be the assignee (or a delegate, or hold a higher role)
Calling it from Apex:
apex
Invocable.Action action = Invocable.Action.createStandardAction('reviewApprovalWorkItem');
action.setInvocationParameter('approvalWorkItemId', workItemId);
action.setInvocationParameter('approvalDecision', 'Approve'); // or 'Reject' — exact capitalization required
action.setInvocationParameter('comments', 'Optional comment text');
List<Invocable.Action.Result> results = action.invoke();
Boolean success = results[0].isSuccess();
We found the documentation for this action almost by accident, linked from the "Recall Approval Submission Action" doc page — it's filed under Salesforce's "Advanced Approvals Standard Invocable Actions" documentation, not anywhere near the ConnectApi Orchestration docs where we were originally looking.
The gotcha that will still bite you: field update sequencing
Getting reviewApprovalWorkItem working is not the end of the story. If your orchestration has logic downstream of the approval step — for example, a Decision element that checks a field on the related record (like "does this need Finance approval next?") to determine routing — that field must already reflect the correct value before you call reviewApprovalWorkItem, not after.
Why: the native Work Guide component's "Approve" button doesn't just mark the work item complete — it runs the entire underlying subflow tied to that approval step (in our case, a subflow that included a Decision element, a field update on the related record, and a Slack notification, all before marking the work item done). When you call reviewApprovalWorkItem directly, you bypass all of that subflow logic and only get the equivalent of "mark this work item done." Any Orchestration Decision element gated on your related record's fields will evaluate immediately once the work item is marked complete — using whatever the field's value is at that exact moment, which may still be stale if you haven't updated it yourself first.
Concretely, for us this meant:
apex
// WRONG ORDER — orchestration's Decision evaluates before the field is updated
Invocable.Action action = Invocable.Action.createStandardAction('reviewApprovalWorkItem');
// ...invoke...
opp.Approval_Status__c = 'Pending Finance Approval'; // too late — Decision already ran
update opp;
apex
// CORRECT ORDER
opp.Approval_Status__c = 'Pending Finance Approval'; // set first
update opp;
Invocable.Action action = Invocable.Action.createStandardAction('reviewApprovalWorkItem');
// ...invoke — Decision now evaluates the correct, already-updated value
We only caught this because the orchestration instance showed Status: Completed (successfully!) but the expected next-stage work item never got created — it took querying the FlowOrchestrationInstance and reading the underlying subflow's Decision logic directly to figure out why.
If your orchestration's subflow also sends notifications (Slack, email) as part of that same subflow logic, be aware those will also be skipped when you bypass the subflow via reviewApprovalWorkItem directly — you'll need to replicate any such notification yourself (as a Flow Action or Apex callout) alongside the field update.
Summary checklist if you're building this yourself
- Don't use ConnectApi.FlowApprovalProcesses for Orchestration Approval Steps — it's a known, tracked bug (W-23702762) for retrieving Approve/Reject actions.
- Don't use classic Approval.process() — wrong object type entirely.
- Do use the standard invocable action reviewApprovalWorkItem, called via Invocable.Action.createStandardAction().
- Capitalize approvalDecision exactly as "Approve" or "Reject".
- Update any fields your orchestration's downstream Decision logic depends on before calling reviewApprovalWorkItem, not after.
- Check whether your approval step's native subflow sends any notifications (Slack/email) as part of its own logic — if so, replicate that separately, since bypassing the subflow means bypassing those too.
Hope this saves someone the hours it cost us to piece together!!!
#Flow-Orchestration #Advanced Approvals #Salesforce_Mobile_App