Stop hardcoding approvers in your Power Automate flows. Learn how to route approval requests dynamically using Azure AD manager lookup, SharePoint configuration tables, and conditional logic based on form input and dollar thresholds — all in one maintainable flow.

Approval workflows fail in one of two ways: they route everything to the same person regardless of context, or they require a developer every time the business rules change. Neither is acceptable when you're building automation that actually serves a real organization. The purchasing manager shouldn't be approving IT equipment requests. The HR director shouldn't be rubber-stamping $200 office supply orders. Yet this is exactly what happens when teams build their first Power Automate approval flow — they hardcode an approver email and call it done.
The good news is that Power Automate gives you everything you need to build genuinely dynamic approval routing — routing that looks up who someone's manager is at runtime, routes based on what they submitted in a form, escalates based on dollar amounts, and pulls approver lists from a configuration table rather than from your flow's action properties. Once you understand the patterns, you can build approval logic that adapts to your organization without touching the flow every time someone changes roles or departments.
By the end of this lesson, you'll be able to route approvals dynamically based on form input, look up a submitter's manager from Azure AD/Microsoft 365, maintain a configurable approver list in SharePoint, and combine all three techniques into a single, maintainable flow. You'll also understand the tradeoffs between each approach so you can pick the right tool for each situation.
What you'll learn:
This lesson assumes you're comfortable with the fundamentals. You should know how to create a cloud flow, add actions, and use dynamic content from previous steps. You should have built at least one basic approval — if not, start with Building Approval Workflows with Power Automate before continuing here. You should also be comfortable with conditions and variables; if either feels shaky, Working with Conditions, Loops, and Variables in Power Automate is the right primer.
You'll need a Microsoft 365 environment with Power Automate access, a SharePoint site where you can create lists, and at least a few test users with populated Azure AD profiles (specifically, manager relationships configured in Azure AD or the Microsoft 365 admin center).
Before writing a single action, it helps to understand the three fundamental patterns for dynamic approver assignment. Most real-world flows use some combination of all three.
Pattern 1: Organizational hierarchy routing. The approver is determined by who the submitter reports to — their direct manager, or the manager's manager for high-value requests. This works great for expense reports, leave requests, and anything that naturally lives within a reporting structure.
Pattern 2: Category or department routing. The approver is determined by what kind of request it is, not who submitted it. IT requests go to the IT manager. Legal contracts go to Legal. This requires a lookup table that maps categories to approver emails.
Pattern 3: Conditional rule-based routing. The approver is determined by evaluating specific field values — typically dollar amounts, risk levels, or flags set in the form. Under $500 goes to a team lead; over $500 goes to department head; over $5,000 goes to the CFO.
The real world almost always combines these. A $300 IT equipment request might go to the submitter's manager for sign-off, then to the IT manager for procurement approval. A $12,000 software license request might skip the manager entirely and go straight to the CFO and IT director in parallel. Let's build each pattern individually, then combine them.
The cleanest way to route approvals through the organizational hierarchy is to look up the submitter's manager at runtime using the Azure AD or Office 365 Users connector. This means you never hardcode a manager's email — if someone changes roles or a team restructures, the flow automatically adapts.
For this lesson, we'll use a Microsoft Forms response as our trigger — a common starting point for employee-facing request processes. If you're working with SharePoint list submissions instead, the same manager lookup pattern applies; you'll just pull the submitter's email from the item's "Created By" field. You can read more about that pattern in Power Automate with SharePoint: Automate Document Approvals.
Create a form called "Employee Purchase Request" with these fields:
Set your flow trigger to "When a new response is submitted" from the Microsoft Forms connector. Follow that with a "Get response details" action using the form ID and the response ID from the trigger.
After getting the response details, add an action from the Office 365 Users connector called Get manager (V2). In the "User (UPN)" field, use the dynamic content for the form responder's email. The Microsoft Forms connector exposes this as "Responder's Email" in the response details.
Note
The "Get manager (V2)" action returns the manager's full profile object — including their email, display name, job title, and department. You access the email through the dynamic content panel as "Mail" from the "Get manager" step, or you can use the expression outputs('Get_manager_(V2)')?['body/mail'] for precision.
The action will fail — and halt your flow — if the user has no manager configured in Azure AD. We'll address this in the error handling section, but for now, know that in organizations where some accounts (shared mailboxes, service accounts) lack manager records, you need a fallback.
Before the manager lookup, initialize a string variable called varApproverEmail with a default value — ideally your team's inbox or a designated fallback approver. This protects you if any downstream lookup fails.
Initialize variable
Name: varApproverEmail
Type: String
Value: approvals-fallback@yourcompany.com
After the "Get manager" step succeeds, add a Set variable action to assign the manager's email:
Set variable
Name: varApproverEmail
Value: [dynamic content: Mail from Get manager (V2)]
Now your approval action can reference varApproverEmail instead of a hardcoded address. When you configure the "Start and wait for an approval" action, use this variable in the "Assigned to" field.
Tip
When entering a variable in the "Assigned to" field of an approval action, make sure you're inserting it via the dynamic content panel rather than typing the variable name as plain text. Power Automate distinguishes between the two, and plain text won't resolve to the variable's value.
Organizational hierarchy works well for some requests, but many processes route to a functional owner regardless of who submitted the request. A contract review goes to Legal. A server provisioning request goes to the IT Operations team. This is category-based routing, and the cleanest implementation uses a SharePoint list as a configuration table.
In your SharePoint site, create a list called Approval Routing Config with these columns:
| Column Name | Type | Notes |
|---|---|---|
| Title | Single line of text | The category or department name |
| ApproverEmail | Single line of text | Primary approver's email |
| BackupApproverEmail | Single line of text | Escalation contact |
| IsActive | Yes/No | Allows deactivating routes without deleting |
Populate it with your routing rules:
| Title | ApproverEmail | BackupApproverEmail | IsActive |
|---|---|---|---|
| IT | itmanager@company.com | ithelpdesk@company.com | Yes |
| Marketing | cmo@company.com | marketing-ops@company.com | Yes |
| Operations | ops-director@company.com | ops-mgr@company.com | Yes |
| Finance | cfo@company.com | controller@company.com | Yes |
| HR | hr-director@company.com | hr-generalist@company.com | Yes |
This list is your single source of truth for routing rules. When the Marketing manager changes, someone with SharePoint edit access updates one cell — no flow editing required.
After your trigger and the "Get response details" step, add a Get items action from the SharePoint connector. Configure it to query your Approval Routing Config list with an OData filter:
Site Address: https://yourcompany.sharepoint.com/sites/AutomationHub
List Name: Approval Routing Config
Filter Query: Title eq '@{outputs('Get_response_details')?['body/rd1a1f6b07bfd4af98fa4ee4703c3f1fc']}'
AND IsActive eq 1
Top Count: 1
Replace the response body path with the actual dynamic content reference for the "Department" field from your form — it'll look something like outputs('Get_response_details')?['body/r...'] where the r... portion is the field's internal ID.
Warning
OData filter queries in SharePoint are case-sensitive and must match the stored values exactly. If your form dropdown says "IT" and your SharePoint list says "it," the filter returns zero results. Standardize your casing across both, or use a toLower() expression on both sides of the comparison.
After the "Get items" action, add a Condition to check whether any results came back:
Condition:
length(outputs('Get_items')?['body/value']) is greater than 0
In the Yes branch, set your varApproverEmail variable using the first item from the returned collection:
Set variable
Name: varApproverEmail
Value: @{first(outputs('Get_items')?['body/value'])?['ApproverEmail']}
In the No branch, leave varApproverEmail at its fallback value and optionally send an alert to your flow admin so they know a routing configuration is missing.
This lookup pattern — query a config table, check for results, set a variable — is one you'll use constantly once you start building configurable flows. It's a more structured approach to the kind of data transformation covered in Transforming and Mapping Data with Power Automate's Compose, Select, and Filter Array Actions.
The third pattern uses conditions to route based on evaluated field values. This is where you implement your organization's approval authority matrix — the policy that says who can approve what based on size, risk, or type.
Before building in Power Automate, map out your rules on paper. For a purchase request, a typical authority matrix looks like this:
This maps to nested conditions in Power Automate. You can think of it as an if/else-if chain.
Initialize a second variable, varApprovalTier, as a string with the value "Standard". This will help you track which rule fired, useful for logging and notifications.
Add a Condition action. Configure the first level:
Condition 1:
[Amount from form response] is less than 500
Yes branch: Set varApproverEmail to the manager's email (already populated from Pattern 1's lookup), set varApprovalTier to "Manager".
No branch: Add another nested Condition:
Condition 2 (inside No of Condition 1):
[Amount from form response] is less than 5000
Yes branch: Set varApproverEmail to the department head email (from Pattern 2's SharePoint lookup), set varApprovalTier to "Department Head".
No branch: Add another nested Condition:
Condition 3 (inside No of Condition 2):
[Amount from form response] is less than 25000
Yes branch: Set varApproverEmail to vp-finance@yourcompany.com, set varApprovalTier to "VP Finance".
No branch: Set varApproverEmail to cfo@yourcompany.com, set varApprovalTier to "CFO".
Key insight
The order of your conditions matters. Always evaluate from the most restrictive to the least restrictive (or smallest to largest threshold). If you evaluate the $25,000 condition first, every request under $25,000 passes it — and you never reach the more granular rules below.
After all conditions resolve, varApproverEmail contains exactly the right approver for this request's amount. Your subsequent "Start and wait for an approval" action uses this variable — no branching required at the approval step itself.
Here's where the real power emerges. Let's build the complete routing logic that uses all three patterns together, with clear sequencing.
1. Trigger: When a new response is submitted (Microsoft Forms)
2. Action: Get response details
3. Action: Initialize variable — varApproverEmail = "fallback@company.com"
4. Action: Initialize variable — varApprovalTier = "Standard"
5. Action: Initialize variable — varManagerEmail = ""
6. Action: Get manager (V2) — User UPN = Responder's Email
[Configure run-after to also run on failure — see error handling below]
7. Action: Set variable — varManagerEmail = Mail from Get manager
8. Action: Get items — Approval Routing Config (filtered by Department)
9. Condition: Did the config lookup return results?
Yes: Set varApproverEmail = config list ApproverEmail (department head)
No: Send admin alert, keep fallback
10. Condition: Amount < 500?
Yes: Set varApproverEmail = varManagerEmail, varApprovalTier = "Manager"
No: Condition: Amount < 5000?
Yes: Keep varApproverEmail as department head, varApprovalTier = "Dept Head"
No: Condition: Amount < 25000?
Yes: Set varApproverEmail = vp-finance@company.com, varApprovalTier = "VP Finance"
No: Set varApproverEmail = cfo@company.com, varApprovalTier = "CFO"
11. Action: Start and wait for an approval
- Title: Purchase Request: [Item Description] ([Amount])
- Assigned To: varApproverEmail
- Details: Full request details including varApprovalTier
12. Condition: Approval outcome = Approved?
Yes: Update SharePoint record, notify submitter of approval
No: Update SharePoint record, notify submitter of rejection with comments
Notice that steps 6-9 run before the amount-based conditions. That's intentional — the department head email gets loaded into varApproverEmail from the config table, then the amount conditions either keep that value (for mid-tier requests) or override it with a higher authority for large requests. The manager's email gets stored separately in varManagerEmail so it's available for low-value requests without losing the department head reference.
Sometimes you need multiple people to approve the same request, either in sequence (one after another) or in parallel (everyone gets notified, first response wins — or you need all to respond). The "Start and wait for an approval" action handles both through the Type field and the semicolon-separated email list in Assigned to.
For parallel approval where everyone must respond, set Type to "Approve/Reject - Everyone must approve" and use a semicolon-separated list:
Assigned To: cfo@company.com;legal@company.com;itmanager@company.com
To build this dynamically, use a Compose action before the approval to concatenate the addresses:
Compose — DynamicApprovers:
@{varApproverEmail};legal@company.com
Or if you want to pull multiple emails from the SharePoint config table:
@{first(outputs('Get_items')?['body/value'])?['ApproverEmail']};@{first(outputs('Get_items')?['body/value'])?['BackupApproverEmail']}
For sequential approval (first person approves, then it goes to the second), you'll use two separate approval actions in sequence. This is covered in depth in Designing Multi-Stage Approval Chains in Power Automate: Sequential, Parallel, and Delegated Sign-Off Patterns.
Tip
Use a Compose action to assemble your final approver string and name it something descriptive like FinalApproverList. Then reference the Compose output in the approval action. This makes debugging dramatically easier — you can see exactly what email string was sent to the approval engine in the run history, without having to mentally reconstruct what the variables resolved to.
Dynamic routing introduces failure modes that hardcoded flows never encounter. You need to handle them explicitly.
If the submitter has no manager in Azure AD, the "Get manager (V2)" action fails and — unless you handle it — the flow stops. Configure the "Set variable — varManagerEmail" action's Run after settings to run on "has failed" as well as "is successful." In that branch, set varManagerEmail to varApproverEmail (the fallback). This way, if the manager lookup fails, the department-based routing still controls the outcome for low-value requests.
To configure this: click the three dots on the "Set variable" action, choose "Configure run after," and check both "is successful" and "has failed."
Alternatively, use a Scope action to wrap the manager lookup and its variable assignment, then configure error handling on the scope. This is the more maintainable approach in complex flows and aligns with the patterns in Master Error Handling and Retry Patterns in Power Automate for Bulletproof Flows.
You already handled the zero-results case in Pattern 2's condition. But there's a subtler issue: what if the Department value from the form doesn't exactly match any Title in your config list? This can happen when form dropdown options aren't perfectly synchronized with SharePoint list entries.
Add a Send an email (V2) action in the "No" branch of your config lookup condition, addressed to your flow admin, with a body like:
Subject: ⚠️ Approval Routing Gap Detected
Body: No approver configuration found for department: [Department value from form].
Request submitted by: [Responder email].
The request has been routed to the fallback approver.
Please add a routing entry for this department in the Approval Routing Config list.
This turns a silent failure into an actionable notification. You'll catch configuration gaps immediately rather than discovering them when a requester complains their request was routed to the wrong person.
The built-in approval action doesn't have a native timeout — it waits indefinitely by default. For production flows, you should pair the approval with a timeout using a parallel branch and a "Delay until" action. When the delay expires before the approval completes, the parallel branch cancels the approval and escalates. This pattern is covered comprehensively in Automating Time-Sensitive Approval Escalations in Power Automate: Using Do Until Loops, Timeout Conditions, and Teams Notifications to Enforce SLA Deadlines.
Dynamic routing means the approver receives requests from many different submitters for many different things. The approval email needs to give them enough context to make a decision without opening another system. A lazy approval email says "You have a pending approval." A good one says:
Title: Purchase Request Approval Required — Adobe Creative Suite License ($2,400)
Details:
Requested By: Sarah Chen (sarah.chen@company.com)
Department: Marketing
Item: Adobe Creative Suite Annual License
Amount: $2,400
Approval Authority Level: Department Head
Business Justification: Required for redesign project launching Q2. Currently sharing
one license across three designers, causing scheduling conflicts and project delays.
Urgency: Standard
Please approve or reject this request directly in this email or through the Power
Automate Approvals center.
Build this details block using a Compose action before the approval step:
Compose — ApprovalDetails:
Requested By: @{outputs('Get_response_details')?['body/responder']}
Department: @{outputs('Get_response_details')?['body/r...department']}
Item: @{outputs('Get_response_details')?['body/r...description']}
Amount: $@{outputs('Get_response_details')?['body/r...amount']}
Approval Level: @{variables('varApprovalTier')}
Urgency: @{outputs('Get_response_details')?['body/r...urgency']}
Justification: @{outputs('Get_response_details')?['body/r...justification']}
Then in the approval action's "Details" field, reference this Compose output. You can also add a link back to the original SharePoint record if you've logged the request there — giving approvers one-click access to any supporting documentation.
For Teams-based approvals where you want approvers to respond directly in a Teams message rather than email, the Adaptive Card approach gives you much richer interaction patterns. See Implementing Adaptive Card-Based Human-in-the-Loop Approvals in Power Automate for that pattern.
Let's put everything together in a structured build exercise. This is the real-world project — by the end, you'll have a functioning flow that routes purchase requests dynamically.
In SharePoint, create the Approval Routing Config list with the columns described in Pattern 2. Add at least three entries covering IT, Marketing, and Operations with real email addresses you can test with.
In Microsoft Forms, create the Employee Purchase Request form with the five fields listed earlier. Make "Total Amount" a number field and "Department" a choice field with options that exactly match your SharePoint list entries.
varApproverEmail (string, fallback email), varApprovalTier (string, "Standard"), varManagerEmail (string, empty).result('Get_manager_(V2)')?['status'] equals "Failed"), set varManagerEmail to varApproverEmail.IsActive eq 1.length(body('Get_items')?['value']) is greater than 0.varApproverEmail to the first item's ApproverEmail.varApproverEmail to varManagerEmail.varApprovalTier appropriately in each branch.varApproverEmail and the Compose output.Submit four test requests:
Check the run history after each submission and verify both varApproverEmail and varApprovalTier resolved correctly. If you're new to reading run history for debugging, Using Power Automate Run History and Flow Checker to Debug and Fix Failing Flows walks through the process in detail.
Mistake 1: Setting the variable after the condition instead of inside it.
This is the most common structural error. If you set varApproverEmail after all your conditions have evaluated, only the last Set variable action runs — overwriting everything the conditions decided. Every variable assignment that changes the routing must live inside the relevant condition branch.
Mistake 2: Forgetting that "Get manager" returns nested properties.
The manager object isn't flat. To get the email, you need outputs('Get_manager_(V2)')?['body/mail'], not just the whole output. Use the dynamic content panel to select "Mail" from the Get manager step rather than inserting the raw output.
Mistake 3: OData filter syntax errors in the SharePoint lookup.
The filter Title eq 'IT' uses single quotes around string values. Using double quotes, or forgetting quotes entirely, causes the filter to fail — usually silently returning all items or zero items. Test your filter directly in the SharePoint REST API browser at https://yoursite.sharepoint.com/sites/yoursite/_api/lists/getbytitle('Approval Routing Config')/items?$filter=Title eq 'IT' before encoding it in Power Automate.
Mistake 4: Using "first()" on an empty array.
If your config lookup returns zero items and you try to use first(outputs('Get_items')?['body/value']), you'll get a runtime error. Always check array length before calling first(). Your condition from Step 4 above handles this — but make sure the Set variable action that uses first() is only inside the Yes branch.
Mistake 5: Building routing logic but not logging the outcome.
If you don't write varApprovalTier or varApproverEmail to your SharePoint request record, you have no audit trail of why each request went where it did. Future you — and your compliance team — will thank present you for adding two "Update item" columns: ApprovalTier and AssignedApprover.
Warning
Don't use the responder's display name to look up their manager — use their email (UPN). Display names can have duplicates, special characters, or formatting differences that cause lookup failures. The Responder's Email field from Microsoft Forms is always the UPN in Microsoft 365 environments.
You've now built the three core patterns for dynamic approval routing in Power Automate: organizational hierarchy routing via manager lookup, category-based routing via a SharePoint configuration table, and conditional rule-based routing via amount thresholds. More importantly, you understand how to combine them — using variables to carry the routing decision through the flow, so the approval action itself stays clean regardless of the complexity upstream.
The key principles to take with you:
varApproverEmail. The approval action reads from it. This separation keeps your flow maintainable.From here, natural next steps include building multi-stage sequential approvals where one approval feeds the next — covered in Designing Multi-Stage Approval Chains in Power Automate: Sequential, Parallel, and Delegated Sign-Off Patterns. If your approval process involves complex multi-condition rules with both form fields and dollar thresholds evaluated simultaneously, Building a Dynamic Multi-Condition Approval Flow in Power Automate takes the patterns from this lesson further. And if your approvals need SLA enforcement — automatic escalation when someone doesn't respond in 48 hours — that's covered in the escalation patterns lesson linked above.