There are three usable multi-approver patterns and one action that delivers all of them. Parallel is a single Start and wait for an approval action with several people in Assigned To, set to Approve/Reject - Everyone must approve or First to respond. Sequential is either the Sequential Approval type or two approval actions with a Condition between them, so a rejection at stage one stops the process. Escalation and reminders are not built in at all: you have to split the work into a flow that creates the approval, a flow that reacts to the response, and a scheduled flow that chases what is still outstanding.
Most multi-approver approvals do not fail. They stall, quietly, in a run that shows no error. This guide covers the three patterns, what the approval action actually hands back, and the specific ways each one breaks in a real organisation.
Home / Guides / Approvals With Multiple Approvers
Last updated: 3 August 2026. By Matty Hatton, founder of Digital Adaption.
A purchase requisition for GBP 18,000 has been sitting somewhere for eleven days. Operations think it is with finance. Finance think it went to the plant manager, who was on leave the week it was raised. Someone opens the flow run history expecting a red cross, and finds a blue clock icon next to Start and wait for an approval. No failure, no warning. The flow is doing exactly what it was told to do, which is wait.
That is the normal failure mode of multi-approver approvals. A single-approver flow is a straight line: ask one person, branch on the answer. The moment a second approver appears you have to settle three questions nobody asks out loud. Do they decide together or in order. Does one rejection kill the whole thing. And what happens when one of them simply does not respond. This guide covers what the action returns once more than one person is involved, the three patterns that work in production, and the failure modes that surface months later. If this is your first approval flow, start with the shared mailbox approval flow guide.
The Approvals connector is not a workflow engine in the way people assume. It is a single action that creates a record, sends notifications, then suspends the run on a webhook until a response arrives. Everything that looks like process design, the routing, the ordering, the chasing, is either an approval type you pick from a dropdown or something you build yourself with ordinary flow logic.
The dropdown is the part most people never read properly. Approval type is the first field on the action and it silently determines when the flow resumes and what shape the output takes. Microsoft's everyone must approve documentation sets out the four core types:
| Approval type | What the action waits for |
|---|---|
| Approve/Reject - Everyone must approve | All approvers approve, or any single approver rejects |
| Approve/Reject - First to respond | Any one approver decides for everyone |
| Custom responses - Wait for all responses | All approvers respond, using your own response options |
| Custom responses - Wait for one response | Any one approver responds, using your own response options |
A fifth option, Sequential Approval, is documented separately under set up sequential approvals and turns the single action into an ordered set of steps. The second reason this goes wrong is that the connector is deliberately thin. There is no reminder setting, no expiry setting in the designer, no delegation and no out-of-office handling. Those are all your problem, and nothing signals that they are missing until a request goes cold.
Before choosing a pattern, understand the output, because this is where multi-approver flows most often produce silently wrong results.
The action surfaces two top-level tokens and one collection. Outcome is the headline result. Response summary is a readable string suitable for an email. Responses is an array with one entry per response received, each carrying the approver's name and email under a responder object, the response, the comments, and both a request date and a response date.
Here is the trap. With one approver, Outcome is the string Approve or Reject and an equality test works. With several it does not. Microsoft describes Outcome for the everyone-must-approve type as "an array of Approve or Reject elements, based on the number of responses to the request", so a condition comparing Outcome to Approve falls down the false branch of logic that looks obviously correct on screen. Test for a rejection instead, or read the collection directly:
@contains(
string(outputs('Start_and_wait_for_an_approval')?['body/outcome']),
'Reject'
)
first(outputs('Start_and_wait_for_an_approval')?['body/responses'])?['approverResponse']
first(outputs('Start_and_wait_for_an_approval')?['body/responses'])?['responder']?['email']
formatDateTime(
first(outputs('Start_and_wait_for_an_approval')?['body/responses'])?['responseDate'],
'dd/MM/yyyy HH:mm'
)
Three further points catch people out. With everyone-must-approve, Responses does not reliably hold one entry per approver, because a single rejection completes the request immediately and the people who had not yet responded never will, so expect gaps when you loop it. The Approve and Reject strings are case-sensitive. And if you switch to a custom response type, approverResponse holds your own option text, so every condition written against Approve stops working the moment somebody renames a button.
One detail with real consequences for finance teams: approval timestamps are always shown in UTC. Between late March and late October a British approval logged at 09:15 is recorded as 08:15. If an auditor will read the record, convert it explicitly with convertTimeZone.
Parallel is the right default when the approvers are peers, the decision is not a hierarchy, and elapsed time matters more than filtering. A capital expenditure request needing sign-off from the department head, the finance business partner and the site engineering manager is a parallel approval. None of them is waiting on the others to form a view.
Use a single Start and wait for an approval action and put every approver into Assigned To, separated by semicolons. The Approvals connector reference confirms the field accepts email addresses (not only the primary address), user principal names and Microsoft Entra ID user IDs, and that you can mix formats in one list. Then pick the approval type against the actual business rule: all three signatures required means Everyone must approve, any one duty manager may authorise means First to respond.
Do not hard-code the list. That is the single biggest cause of dead approval flows a year after go-live. Resolve it at run time from a group or a configuration list and join it:
join(body('Select_approver_emails'), ';')
where Select_approver_emails is a Data Operations Select action mapping the mail column out of an Office 365 Groups list-members call or a SharePoint configuration list. When the panel changes, the change is made in one place by someone allowed to make it, not by editing a production flow.
Microsoft's parallel approvals tutorial uses a different technique: separate approval actions on branches added with Add a parallel branch. Be deliberate about which you want.
| One action, several people | Several actions on branches | |
|---|---|---|
| Records created | One approval, one request per person | One independent approval per branch |
| Early exit on rejection | Yes, with everyone-must-approve | No, every branch runs to completion |
| Per-approver follow-on steps | Awkward, requires looping Responses | Natural, each branch has its own condition |
| Best for | One decision needing several signatures | Independent decisions with different downstream work |
The published connector limitations bite hardest here. Custom response approvals sent to a large group with everyone-must-approve can fail outright on data size limits, so keep those panels small. Approval emails are only actionable when sent from Microsoft's standard address, so a branded notification produces an email that looks right and cannot be actioned. And the flow creator is always shown in the approval details regardless of the Requestor field, an anti-spoofing measure that confuses every finance team seeing an IT service account named as the requester of a purchase order.
Sequential is correct when later approvers should not be troubled unless earlier ones agree, or when the stages ask genuinely different questions. Line manager confirms entitlement, finance confirms the budget exists, a director confirms above a threshold. Asking all three at once wastes a director's attention on requests the line manager was always going to refuse.
Add Start and wait for an approval, choose Sequential Approval, fill Assigned To - 1 and use Add new item for each further step. Step one is asked first and step two is only raised if step one approves.
The documented limitation is short and important: you cannot assign the same approver to different steps of the same approval. If your path is "manager, then manager's manager" and in a small firm those resolve to the same person, the configuration will not stand. Resolve both with Get manager (V2), compare them, and fall back to a single-step approval when they match.
This is the pattern in Microsoft's sequential approvals tutorial, and it remains the right answer whenever stages are conditional. The built-in Sequential type runs every step you configure, so it cannot skip a stage based on value. Two actions and a condition can.
Trigger: When an item is created (SharePoint)
Get manager (V2) -> line manager
Start and wait for an approval -> stage 1, line manager
Condition: contains(string(outcome), 'Reject')
True -> mark Rejected, notify requester, terminate
False -> Condition: Value gt 5000
True -> Get manager (V2) 2, Start and wait for an approval 2
False -> mark Approved
Two habits make this maintainable. Rename every action to what it means, because Start_and_wait_for_an_approval_2 tells the next person nothing and the name is baked into every expression referencing it. And write the stage-one decision to your source record before starting stage two, so a run that dies between stages still leaves evidence of the first signature.
Sequential is the pattern most exposed to elapsed time, because the clock runs in series, and the one where a stalled middle stage is hardest to spot: the requester sees "in progress" and cannot tell which of three people to chase. Write the current stage back to the source record at every step.
Neither previous pattern handles the most common real complaint, which is that nobody responded at all. Approvers get one notification when the request is raised and nothing afterwards. There is no setting to change that.
The instinct is to add a parallel branch containing a Delay and a reminder email. It does not work, because Power Automate has no concept of one branch winning and cancelling the other. Both run to completion, so the reminder goes out whether or not the approval was granted, and you chase people for decisions they made three days earlier.
Stop trying to hold the whole process inside one run. This mirrors Microsoft's own recommendation for anything long-running, which is to store approvals in Dataverse and use two flows, one to send and one to act on responses.
Due dates are arithmetic on the tracking row:
addDays(utcNow(), 3, 'yyyy-MM-dd')
The split has a second benefit worth more than the reminders. Each flow is short, so each run is legible, a failure in the chaser cannot leave an approval in limbo, and nothing depends on a single run surviving for weeks.
Escalation and reassignment are different things. Notifying a manager is easy. Moving a pending approval to someone else is not exposed in the designer at all.
Two things are true. The action has an Enable reassignment option letting the assigned approver hand the request on from the approvals centre. Turn it on: it costs nothing and solves the holiday case, provided the approver is present enough to use it. If the approver cannot act at all, the only supported route is Dataverse. Approvals live in the environment in the Approval table (msdyn_flow_approval) and the user-owned Approval Request table (msdyn_flow_approvalrequest), which carries an Allow Reassignment flag, a Reassigned From lookup and a Reassigned status reason. Changing the owner of an active Approval Request row moves the pending request.
Action: List rows (Microsoft Dataverse)
Table: Approval Requests
Select: msdyn_flow_approvalrequest_name,statecode,statuscode,ownerid,createdon
Filter: statecode eq 0
Be clear about the cost first. The Microsoft Dataverse connector is classed as Premium for Power Automate while the Approvals connector is Standard, so the moment your escalation logic touches those tables the flow needs a paid licence it did not need before. How to size that is covered in the Power Automate licensing guide.
| Situation | Pattern | Configuration |
|---|---|---|
| Several signatures, all required, peers | Parallel | One action, Everyone must approve |
| Any one of a rota can authorise | Parallel | One action, First to respond, membership resolved at run time |
| Fixed hierarchy, always the same stages | Sequential | Sequential Approval type |
| Stages depend on value, risk or category | Sequential | Two actions with a Condition between |
| Approvers are slow or absent | Escalation | Three flows: initiator, responder, chaser |
| Process may run beyond a month | Escalation | Create an approval plus Wait for an approval |
Most flows that survive contact with a real organisation are a parallel or sequential core wrapped in the escalation structure. The first two decide who signs. The third decides what happens when they do not.
Approvers outside the organisation. You can send approvals to Microsoft Entra B2B guests, but actionable approval emails are not supported for guest users. They receive a notification and must open the Power Automate portal to respond, which most external suppliers will not do unprompted. Send a separate explanatory email alongside.
Group mailboxes. Assigned To is documented as taking user identities, not distribution groups, so expand the membership yourself and pass a semicolon-joined list of individuals with First to respond. That also gives you what a group address never does: a record of which specific person decided. Approvals raised from a shared mailbox have their own quirks, covered in the shared mailbox approval guide.
Custom responses. Options beyond Approve and Reject are often better modelled than a forced binary, particularly for a "return for amendment" state. Two cautions: rewrite every downstream condition, and keep the panel small, because custom responses with everyone-must-approve are documented as able to fail on large groups.
Cancelling and bulk runs. A withdrawn requisition does not cancel its approval; the sender must cancel from Approvals, Sent, Cancel, and cancellation is supported on the Create an approval action. Build that path deliberately if source records can be voided. Separately, the connector throttles approval creation at 50 create requests per flow per 60 seconds, so a month-end job raising 400 approvals in a loop needs concurrency control or a delay inside the loop.
Work down this list before rebuilding anything. The cause is usually in the first four.
Six months after go-live somebody asks who approved a particular purchase. The natural place to look is the flow run, and it is not there: run history is retained for 30 days from the run start time. The approval records persist in the Dataverse tables, but reading them needs the premium connector and they were never designed as a reporting surface.
The fix is cheap at build time and expensive afterwards. At the moment of decision, write one row of your own to SharePoint, SQL or Dataverse holding the source record reference, the approver's email, the response, the comments, the response date converted out of UTC, and the approval type used. Five minutes per flow, and the difference between answering an auditor in a minute and reconstructing an authorisation from memory.
Most approval work I get called into is not really an approval problem. It is a process nobody wrote down, encoded into a flow by whoever was available, with the approver list typed into a field. If that describes yours, Power Automate consultancy is the place to start: I map what the process actually is, decide which of these three patterns fits, and build it so the approver list is data rather than configuration.
For sign-off routing, escalation design and audit records that outlive the flow run, see approval workflow consultancy. If the flow you are building has just shown you a premium connector warning, the Power Automate licensing guide explains what triggered it and what you need to buy. Where approvals are one symptom of wider manual handoffs, business process automation covers the broader piece. Delivery is ISO 9001 certified, which here mostly means the documentation describes what was built rather than what was intended.
Put every approver into the Assigned To field of the Start and wait for an approval action, separated by semicolons. The field accepts email addresses, user principal names and Microsoft Entra ID user IDs, and you can mix them. Then pick the approval type that matches the rule: Approve/Reject - Everyone must approve if all of them must agree, or Approve/Reject - First to respond if any one of them can decide.
Parallel asks every approver at the same time and waits for either all responses or the first, depending on the approval type. Sequential asks approver one first and only raises approver two if approver one approves, so a rejection at stage one stops the process before later approvers are contacted. Sequential costs elapsed time. Parallel costs the ability to filter out obviously wrong requests early.
No. The Start and wait for an approval action has no reminder or escalation setting in the designer. Approvers get one notification when the request is created and nothing after that. If you need chasing, you have to build it, normally by splitting the process into a flow that creates the approval, a flow that reacts to the response, and a scheduled flow that finds items still outstanding past their due date.
Nothing visible, which is the problem. Approval requests are owned by an individual user record, so if that account is disabled the request stays with it and nobody else can act. The run waits with no error until it hits the 30 day run duration limit, at which point the pending step times out. Resolve approvers at run time from a group or lookup table rather than typing an address into the action, and enable reassignment.
Because Outcome is not a single word once more than one person is involved. Microsoft documents it as an array of Approve or Reject elements based on the number of responses, so a condition testing Outcome is equal to Approve will not match. Test the Responses collection instead, or use a contains check against the string form of Outcome. Custom response types make it worse, because approverResponse then holds your own option text.
Flow run history is retained for 30 days from the run start time, so the run showing the approver, comment and timestamp is gone before most audits. The approval records live in Dataverse tables in the environment, but querying them needs the premium Microsoft Dataverse connector. For a defensible audit trail, write your own row at the moment of decision, and remember that approval timestamps are always UTC.
The three patterns are not difficult. What makes multi-approver approvals expensive is that the wrong choice does not announce itself. A first-to-respond type on a flow that needed three signatures runs perfectly for a year, and the failure is found by an auditor rather than a user.
If you have an approval process that is stalling, or one you are about to build and want to get right before it becomes load-bearing, get in touch. Half an hour is normally enough to work out which pattern you need and where the current one leaks.