Wicked Smart Data
LearnInsightsAboutContact
Sign InLet's Build
LearnInsightsAboutContact
Sign InLet's Build
Wicked Smart Data

Intelligence, automation, and expert execution — plus an elite library of free knowledge. We turn complexity into competitive advantage.

Start a conversation

Platform

  • Learning Paths
  • Insights
  • RSS Feed

Company

  • About
  • Contact
  • Work With Us

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Wicked Smart Data. All rights reserved.

Intelligence · Automation · Advantage

All Insights
Power Automate

Automating Recurring Approval Reminders in Power Automate: Escalating Overdue Requests, Notifying Delegates, and Closing Stale Approvals Automatically

Most approval workflows send one email and go silent. This lesson teaches you to build a persistent, self-healing approval system with scheduled reminders, SLA-based escalation, OOF-aware delegate routing, and automatic closure of stale requests — all backed by SharePoint state management.

🔥 Expert26 min readOct 6, 2026Updated Oct 6, 2026
Automating Recurring Approval Reminders in Power Automate: Escalating Overdue Requests, Notifying Delegates, and Closing Stale Approvals Automatically
On this page
  • Introduction
  • Prerequisites
  • Understanding the Architecture Before Writing a Single Action
  • Designing Your SharePoint Tracking List
  • Building the Originating Flow: Submit and Track
  • Building the Watcher Flow: Scheduled Reminders with Escalation Logic
  • Step 1: Query Pending Approvals
  • Step 2: Loop and Calculate Age
  • Step 3: The Escalation Decision Tree
  • Detecting Out of Office and Routing to Delegates
  • Building the Completion Flow: Handling Responses
  • Handling the Teams Notification Channel
  • Managing State Transitions and Idempotency
  • The Expression Layer: Working with Dates and Dynamic Content
  • Building an Admin Visibility Dashboard
  • Hands-On Exercise
  • Common Mistakes & Troubleshooting
  • Summary & Next Steps
  • Automating Recurring Approval Reminders in Power Automate: Escalating Overdue Requests, Notifying Delegates, and Closing Stale Approvals Automatically

    Introduction

    Picture this: a purchase request worth $47,000 has been sitting in your CFO's inbox for nine days. The vendor's discount window closes tomorrow. Nobody noticed because the CFO was at a conference, the original requestor assumed someone was handling it, and your approval workflow politely sent a single email on day one and then went completely silent. This is the failure mode that kills otherwise well-designed approval systems — not the logic of who approves what, but the complete absence of escalation, delegation, and self-healing when humans go quiet.

    Building an approval workflow that routes requests to the right person is relatively straightforward once you understand the basics. But building one that persists — that reminds approvers on a schedule, automatically escalates to a manager after a deadline, notifies a delegate when someone is out of office, and ultimately closes or redirects stale requests rather than leaving them to rot indefinitely — that's the engineering challenge that separates production-grade automation from demo-ware. By the end of this lesson, you'll have a complete, working pattern for doing exactly that, implemented in Power Automate using realistic SharePoint-backed state management, recurrence triggers, and dynamic escalation logic.

    What you'll learn:

    • How to architect a reminder system that uses SharePoint as a state store to track approval age, escalation level, and delegate assignment
    • How to build a scheduled flow that queries overdue approvals and sends tiered reminders with appropriate urgency
    • How to implement escalation logic that routes to a manager or alternate approver when a deadline is breached
    • How to detect Out of Office status using the Microsoft 365 connector and dynamically redirect approvals to a configured delegate
    • How to automatically close or reject stale approvals after a maximum threshold, update records, and notify stakeholders

    Prerequisites

    You should arrive at this lesson with solid working knowledge of Power Automate fundamentals. Specifically, you need to be comfortable with:

    • Building multi-step automated cloud flows and understanding how scheduled flows work — if recurrence triggers are new to you, read Scheduling and Managing Time-Based Flows in Power Automate: Recurrence Triggers, Time Zones, and Business Hours Logic first
    • Working with the built-in Approvals connector, including Start and wait for an approval and Wait for an approval — the lesson Building Approval Workflows with Power Automate covers this well
    • SharePoint list operations: Get Items, Update Item, and Filter queries
    • Conditions, switches, and the expression language for date arithmetic — brush up at Working with Conditions, Loops, and Variables in Power Automate if needed
    • Comfortable reading and writing expressions like addDays(), utcNow(), formatDateTime(), and int()

    Understanding the Architecture Before Writing a Single Action

    The most common mistake when building reminder systems is trying to encode all the logic inside the original approval flow — the one that fires when a request is submitted. People add a Delay action followed by a reminder email, then another Delay, then another email. This feels logical but it's deeply fragile. A delayed flow run that spans multiple days is sitting in Power Automate's backend waiting for a timer to expire. If your flow is turned off, re-saved, or hits a platform error during that wait, the entire thread dies silently. You've also got no way to inspect "which approvals are currently delayed" or "how many reminders have been sent" without digging through run history.

    The better architecture separates concerns completely:

    1. The originating flow creates the approval request and writes a tracking record to a SharePoint list. It then exits. It doesn't wait. It doesn't delay.
    2. A scheduled flow (the "watcher") runs on a recurrence — every few hours or once per business day — queries the SharePoint list for pending approvals, and performs reminder/escalation logic based on the data it finds there.
    3. A completion flow triggers when the SharePoint tracking record changes to a terminal state (Approved, Rejected, Cancelled) and handles downstream notifications.

    This architecture is observable, restartable, and debuggable. Your SharePoint list is your source of truth. You can look at it any time and know exactly where every approval stands.

    Key insight

    The Start and wait for an approval action is deceptively convenient for simple cases, but it holds an entire flow run open for potentially days. At scale this creates problems with run history bloat and makes escalation nearly impossible to implement cleanly. The pattern we're building here uses Create an approval (non-blocking) plus a SharePoint tracking table, which gives you full control.


    Designing Your SharePoint Tracking List

    Your SharePoint list is the engine of this entire system. Get the schema right and the rest of the build is straightforward. Get it wrong and you'll be rebuilding it halfway through.

    Create a SharePoint list called PendingApprovals with the following columns:

    Column Name Type Purpose
    Title Single line of text Request ID or description
    RequestorEmail Single line of text Who submitted the request
    ApproverEmail Single line of text Current assigned approver
    DelegateEmail Single line of text Alternate approver if escalated
    ManagerEmail Single line of text Escalation target
    ApprovalId Single line of text Power Automate approval ID
    SubmittedDate Date and Time When the request was submitted
    LastReminderSent Date and Time When we last sent a reminder
    ReminderCount Number How many reminders have been sent
    EscalationLevel Number 0 = initial, 1 = first escalation, 2 = final
    Status Choice Pending, Escalated, Expired, Approved, Rejected
    RequestAmount Currency Dollar value (for this example)
    DepartmentCode Single line of text For routing logic
    MaxDaysAllowed Number Per-request SLA (e.g., 5 or 10 days)
    Notes Multiple lines of text Audit trail notes

    The MaxDaysAllowed column is crucial — it lets you implement different SLAs for different request types. A $500 expense reimbursement might have a 10-day window while a $50,000 capital expenditure request might need approval within 3 business days. You bake the SLA into the record at submission time rather than hardcoding it in the flow.

    Tip

    Add an index on the Status column in SharePoint list settings. When your list grows to hundreds of pending approvals, the filter query your scheduled flow runs every few hours will thank you. Lists without indexed columns run full table scans on filtered Get Items calls.


    Building the Originating Flow: Submit and Track

    The originating flow fires when a new request is submitted — in our example, from a Microsoft Forms submission or a Power Apps canvas app. Its job is simple: create the approval, write the tracking record, send a confirmation to the requestor, and exit.

    In your flow, after whatever trigger you're using, add a Create an approval action (not "Start and wait"). Configure it as follows:

    • Approval type: Approve/Reject - First to respond (or Everyone must approve for a sequential pattern — see Designing Multi-Stage Approval Chains in Power Automate: Sequential, Parallel, and Delegated Sign-Off Patterns for multi-stage variants)
    • Title: A dynamic title using the request ID and amount — something like Expense Approval Required: [RequestID] — $[Amount]
    • Assigned to: The approver email from your routing logic
    • Details: Full context including requestor name, department, amount, and business justification
    • Item link: A direct link to the SharePoint item or the originating form response
    • Due date: addDays(utcNow(), triggerOutputs()?['body/MaxDaysAllowed']) — set the approval's native due date to match your SLA

    After the Create action, capture the Approval ID from its output. Then add a Create item action to your PendingApprovals SharePoint list:

    Title:              [Request description]
    RequestorEmail:     [From trigger or form]
    ApproverEmail:      [Assigned approver]
    ManagerEmail:       [From HR data or form field]
    ApprovalId:         outputs('Create_an_approval')['body/approvalId']
    SubmittedDate:      utcNow()
    LastReminderSent:   utcNow()
    ReminderCount:      0
    EscalationLevel:    0
    Status:             Pending
    MaxDaysAllowed:     [From trigger data]
    

    Warning

    The Create an approval action generates the approval and sends the initial notification email automatically. Do not send a separate "You have a pending approval" email after this action — the approver will receive two notifications for the same request and immediately start ignoring your reminders.

    Now send a confirmation to the requestor using a Send an email (V2) action. Keep it simple: "Your request [ID] has been submitted and is pending approval. You will be notified when a decision is made." Include an estimated decision date based on MaxDaysAllowed.

    The originating flow is done. It runs in seconds and exits cleanly.


    Building the Watcher Flow: Scheduled Reminders with Escalation Logic

    This is the heart of the system. The watcher flow runs on a recurrence — let's say every 4 hours during business hours. You could also run it daily if your SLAs are measured in days rather than hours. For the configuration of recurrence triggers with business hours awareness, the lesson on Scheduling and Managing Time-Based Flows in Power Automate: Recurrence Triggers, Time Zones, and Business Hours Logic covers exactly how to set hours-of-day constraints.

    Step 1: Query Pending Approvals

    Add a Get items action targeting your PendingApprovals list with this OData filter:

    Status eq 'Pending' or Status eq 'Escalated'
    

    Set the top count to 500 and ensure you have pagination enabled if you might exceed that. Add a Compose action to calculate today's date as a string for use in expressions:

    formatDateTime(utcNow(), 'yyyy-MM-dd')
    

    Step 2: Loop and Calculate Age

    Add an Apply to each loop over the items returned. Inside the loop, calculate the age of the request in days using integer division:

    div(
      sub(
        ticks(utcNow()),
        ticks(item()?['SubmittedDate'])
      ),
      864000000000
    )
    

    Store this in a variable called RequestAgeDays that you initialize before the loop (as an integer, default 0). Similarly calculate DaysSinceLastReminder:

    div(
      sub(
        ticks(utcNow()),
        ticks(item()?['LastReminderSent'])
      ),
      864000000000
    )
    

    Note

    The ticks() function returns the number of 100-nanosecond intervals since January 1, 0001. Subtracting two tick values and dividing by 864000000000 (which is 10,000,000 ticks per second × 86,400 seconds per day) gives you elapsed days as an integer. This is more reliable than string-parsing date differences, especially across month and year boundaries.

    Step 3: The Escalation Decision Tree

    Now build a condition that branches on RequestAgeDays relative to MaxDaysAllowed from the current item. This is where your business logic lives.

    Branch A: Request has exceeded MaxDaysAllowed (Auto-close)

    RequestAgeDays >= item()?['MaxDaysAllowed'] + 2
    

    If this is true, the request has been pending for the full SLA period plus a 2-day grace period. Auto-close it:

    1. Cancel the approval using the Cancel an approval action with the stored ApprovalId
    2. Update the SharePoint item: Set Status to Expired, append to Notes: Auto-closed after [RequestAgeDays] days with no response.
    3. Send an email to the requestor: "Your request [ID] was automatically closed after [X] days without an approval decision. Please resubmit if still needed."
    4. Send an email to the approver and their manager: "A pending approval request [ID] was automatically closed due to inactivity."

    Branch B: Request has passed MaxDaysAllowed but is in grace period

    RequestAgeDays >= item()?['MaxDaysAllowed'] and RequestAgeDays < item()?['MaxDaysAllowed'] + 2
    

    This is the escalation window. If EscalationLevel is 0 or 1, escalate now:

    1. Check EscalationLevel: If it's 0, escalate to the manager. If it's 1, the approval is already escalated — this is a second escalation reminder.

    2. For EscalationLevel 0 (first escalation):

      • Send email to ManagerEmail: "A request requiring your team's approval has exceeded its SLA. Immediate action is needed."
      • Reassign the approval by cancelling the current one and creating a new approval assigned to ManagerEmail (capturing the new ApprovalId)
      • Update SharePoint: Status = Escalated, EscalationLevel = 1, ApproverEmail = ManagerEmail, LastReminderSent = utcNow()
      • Send email to original approver: "This approval has been escalated to your manager due to SLA breach."
    3. For EscalationLevel 1 (already escalated, still pending):

      • Send a high-urgency reminder to ManagerEmail
      • Update LastReminderSent = utcNow(), increment ReminderCount
      • Append "Second escalation reminder sent" to Notes

    Branch C: Request is within SLA but overdue for a reminder

    DaysSinceLastReminder >= 2 and RequestAgeDays < item()?['MaxDaysAllowed']
    

    This is your standard reminder loop. Send a reminder, increment the counter:

    1. Build the reminder email with context:

      • Days elapsed vs. SLA days allowed
      • Direct link to the approval (the approval URL from the original Create an approval output — store this in your SharePoint record as an ApprovalLink column)
      • A clear call to action
    2. Vary the urgency by ReminderCount:

      • ReminderCount = 0: Friendly nudge. "Just a reminder that you have a pending approval."
      • ReminderCount = 1: Moderate urgency. "This approval is approaching its deadline."
      • ReminderCount >= 2: High urgency. "Action required: This approval will be escalated in [X] days."
    3. Update the SharePoint item: Increment ReminderCount, set LastReminderSent = utcNow()

    Here's what that email subject escalation looks like as expressions in a Switch block based on ReminderCount:

    Case 0: 'Reminder: Approval Needed for [Title]'
    Case 1: 'Second Reminder — Approval Deadline Approaching'
    Default: concat('URGENT: Approval Required — ', sub(item()?['MaxDaysAllowed'], int(variables('RequestAgeDays'))), ' Day(s) Remaining')
    

    Key insight

    The reason you check DaysSinceLastReminder >= 2 rather than sending a reminder on every watcher run is that your watcher runs every 4 hours. Without a cooldown check, an approver would receive 6 reminders per day — which guarantees they'll create an inbox rule to delete your approval emails. Two-day reminder intervals are aggressive enough to maintain urgency without becoming noise.


    Detecting Out of Office and Routing to Delegates

    Here's a feature most teams never implement but everyone wishes they had: if the approver is out of office, automatically notify their delegate instead of letting the request sit unanswered.

    The Microsoft 365 Outlook connector has a Get mail tips for a mailbox action that returns OOF (Out of Office) status. Add this inside your Apply to each loop, before the escalation decision tree, when the item's EscalationLevel is 0 (i.e., it hasn't been escalated yet).

    1. Add Get mail tips for a mailbox with the ApproverEmail from the current item.
    2. Add a Condition: outputs('Get_mail_tips_for_a_mailbox')['body/value'][0]['isOutOfOffice'] equals true

    If the approver is OOF:

    • Check whether DelegateEmail is already populated in the SharePoint record. If it's not, try to retrieve the delegate from the approver's OOF auto-reply message (the mail tips response includes the OOF message text — you can parse it for a delegate mention, though this is brittle). More reliably, look up a DelegateDirectory SharePoint list you maintain separately that maps each person to their designated alternate.
    • If you find a delegate: Cancel the current approval, create a new one assigned to the delegate, update the SharePoint record (ApproverEmail = delegate, DelegateEmail = original approver), send email to delegate explaining context.
    • If no delegate is found: Send the reminder anyway and flag in Notes that approver appears to be OOF with no configured delegate. This surfaces in your admin dashboard (see the section below on monitoring).

    The lookup against a delegate directory list looks like this:

    Add a Get items action against your DelegateDirectory list with filter:

    EmployeeEmail eq '[ApproverEmail from current item]'
    

    Then in a condition, check length(body('Get_items_DelegateDirectory')['value']) > 0. If true, take first(body('Get_items_DelegateDirectory')['value'])['DelegateEmail'] as your delegate.

    Warning

    When you cancel and recreate an approval to change the assignee, you lose the original approval thread in the Approvals hub for that user. Consider instead sending the delegate a Teams Adaptive Card notification with approve/reject buttons rather than a formal approval. This preserves the audit trail while still enabling action. The lesson on Implementing Adaptive Card-Based Human-in-the-Loop Approvals in Power Automate: Dynamic Forms, Contextual Data Injection, and Response Handling walks through exactly this pattern.


    Building the Completion Flow: Handling Responses

    The completion flow fires when an approval decision is made. The cleanest trigger for this is When an item is modified on your PendingApprovals SharePoint list — but that fires on every modification, including reminder count updates. Instead, use a separate flow that watches for the approval to complete.

    The better approach: use a dedicated When an approval is completed trigger (under the Approvals connector), or — in the non-blocking architecture — add a parallel branch to the originating flow that runs Wait for an approval against the original ApprovalId. This branch can run for days waiting for the response; when it gets one, it fires.

    Here's the tricky part of non-blocking approvals: you have an approval ID from Create an approval, and you need to wait for it in a separate path. Structure your originating flow with a Parallel branch after the Create action. The left branch writes to SharePoint and sends the confirmation email (quick and exits). The right branch uses Wait for an approval with the ApprovalId — this branch sits dormant until the approver responds.

    When the Wait action finally resolves:

    1. Capture the outcome: outputs('Wait_for_an_approval')['body/outcome'] — this is either "Approve" or "Reject"
    2. Update the SharePoint item: Set Status to Approved or Rejected, set a CompletedDate column, append the approver's comments to Notes
    3. Send notification to requestor with the decision and any comments
    4. Trigger downstream actions based on outcome:
      • Approved: Create a PO in your financial system, send to procurement, update a Power BI dataset
      • Rejected: Notify requestor with reason, offer resubmission guidance
    5. If the item was already Expired: This is an edge case — the approver responded after the auto-close. Handle it gracefully: send an email noting "This request was closed due to inactivity; your response has been recorded but the request must be resubmitted if action is still needed."

    Tip

    Store the approval response comments in your SharePoint list's Notes field in a structured format: [Date] [Approver]: [Comment]. This creates a readable audit trail directly in the list without needing to query Power Automate run history — which is essential because run history only persists for 30 days by default.


    Handling the Teams Notification Channel

    Your approval system becomes dramatically more useful if you also post status updates to a Teams channel — both for individual notifications and for a shared team channel that an operations manager can monitor. For an approval that's been sitting for 5 days with escalation to a manager, the manager should see this in Teams, not just email.

    Using Using Power Automate with Microsoft Teams: Automate Notifications, Approvals, and Channel Messages, add a Post message in a chat or channel action inside your escalation branch:

    • Post in: Channel
    • Team: Your operations or management team
    • Channel: #approval-escalations (create this channel and add relevant managers)
    • Message: Build an Adaptive Card or plain text with: Request ID, description, amount, days pending, requestor, original approver, escalation reason

    For individual approvals, use Post a message in chat directly to the manager being escalated to. People respond faster to Teams pings than to emails in many organizations, and the combination of both channels maximizes your response rate.


    Managing State Transitions and Idempotency

    The watcher flow runs every 4 hours. Over a 10-day approval window, it'll run approximately 60 times against the same pending record. You must design every action inside the loop to be idempotent — meaning running it multiple times has the same effect as running it once.

    The LastReminderSent and DaysSinceLastReminder check is your primary idempotency guard. But you need to protect against race conditions too — what if the watcher runs twice simultaneously? This is unlikely on a 4-hour schedule but possible if you manually trigger the flow for testing.

    Add a Status check at the very start of your loop, before any action:

    item()?['Status'] eq 'Pending' or item()?['Status'] eq 'Escalated'
    

    If the status has changed since the Get Items query ran (e.g., someone approved it 3 minutes ago), skip this item entirely. You can implement this with a condition that wraps the entire escalation logic.

    For the auto-close action specifically, add an additional check: attempt to cancel the approval, but use a "Configure run after" setting on the Cancel action to handle both success and failure gracefully. If the approval is already cancelled or approved, the Cancel action will fail — configure it to continue regardless and simply update the SharePoint record. For detailed error handling patterns like this, see Master Error Handling and Retry Patterns in Power Automate for Bulletproof Flows.

    Key insight

    The most dangerous state in any approval system is "ambiguous" — where Power Automate thinks the approval is pending but SharePoint says it's been decided, or vice versa. Your Notes field is your insurance policy here. Log every state transition with a timestamp and the reason: "2025-03-15T14:22Z — Auto-closed after 12 days. Approval ID: AP-1234". If you ever need to audit what happened, you'll have it.


    The Expression Layer: Working with Dates and Dynamic Content

    The date arithmetic in this flow relies on expressions you need to get exactly right. Let's walk through the specific expressions you'll use repeatedly.

    Calculating request age in days (integer):

    div(
      sub(
        ticks(utcNow()),
        ticks(items('Apply_to_each')?['SubmittedDate'])
      ),
      864000000000
    )
    

    Days remaining before SLA breach:

    sub(
      items('Apply_to_each')?['MaxDaysAllowed'],
      div(
        sub(ticks(utcNow()), ticks(items('Apply_to_each')?['SubmittedDate'])),
        864000000000
      )
    )
    

    Formatted deadline date (for email body):

    formatDateTime(
      addDays(items('Apply_to_each')?['SubmittedDate'], items('Apply_to_each')?['MaxDaysAllowed']),
      'dddd, MMMM d, yyyy'
    )
    

    Checking if a string field is empty (for DelegateEmail check):

    empty(items('Apply_to_each')?['DelegateEmail'])
    

    Building the Notes append string:

    concat(
      items('Apply_to_each')?['Notes'],
      '\n',
      formatDateTime(utcNow(), 'yyyy-MM-dd HH:mm'),
      ' UTC — Reminder #',
      string(add(items('Apply_to_each')?['ReminderCount'], 1)),
      ' sent to ',
      items('Apply_to_each')?['ApproverEmail']
    )
    

    For a deep dive on the full expression language covering string manipulation, date math, and array functions, Mastering Dynamic Expressions and the Power Automate Formula Language: String, Date, and Array Functions for Real-World Data Manipulation is the reference you'll want bookmarked.


    Building an Admin Visibility Dashboard

    A reminder system that operates invisibly is one that nobody trusts. Build a simple visibility layer by creating a SharePoint view on your PendingApprovals list filtered to Status = Pending or Escalated, sorted by SubmittedDate ascending. Share this view link in your operations Teams channel. Anyone can see the backlog in real time without touching Power Automate.

    Go further: add a daily digest flow that runs every morning at 8 AM and posts a summary to your operations channel:

    1. Get items from PendingApprovals where Status is Pending or Escalated
    2. Filter that result to items where RequestAgeDays >= 3 (anything approaching SLA)
    3. Build a Compose action with an HTML table using join() and select() from the array:
    join(
      select(
        body('Get_items')?['value'],
        item(),
        concat(
          item()?['Title'], ' | ',
          item()?['ApproverEmail'], ' | ',
          string(div(sub(ticks(utcNow()), ticks(item()?['SubmittedDate'])), 864000000000)),
          ' days pending'
        )
      ),
      '\n'
    )
    
    1. Post this as a Teams message or email to your approvals admin distribution list

    This daily digest is the difference between an automated system your leadership trusts and one that generates anxiety because nobody can see what's happening inside it.


    Hands-On Exercise

    Build the complete approval reminder system end-to-end in a development environment using this realistic scenario:

    Scenario: Your IT department processes software license requests. Requests under $1,000 must be approved by the IT manager within 5 days. Requests over $1,000 require the IT director and must be decided within 3 days. All pending requests older than their deadline + 2 days auto-close.

    Build tasks:

    1. Create the PendingApprovals SharePoint list with all columns from the schema above. Add a DelegateDirectory list with EmployeeEmail and DelegateEmail columns. Add test data: 3 entries with SubmittedDate values of 1, 4, and 8 days ago, all with Status = Pending.

    2. Build the originating flow triggered by a manual HTTP request (for testing) that creates an approval and writes a SharePoint record. Use triggerBody()?['amount'] and triggerBody()?['requestorEmail'] as inputs. Set MaxDaysAllowed to 5 if amount < 1000, else 3.

    3. Build the watcher flow with a recurrence trigger (set to every 1 hour for testing, you'll change it later). Inside the loop, implement all three branches: auto-close, escalate, and standard reminder. Test each branch by manipulating your test data's SubmittedDate to fall into the different conditions.

    4. Verify the OOF check works by populating one record with your own email as ApproverEmail and turning on OOF in Outlook. Run the watcher and confirm it routes to the delegate.

    5. Build the daily digest flow that posts a summary to a test Teams channel.

    Validation criteria: After building and testing, your SharePoint list should accurately reflect the state of each test item, your email inbox should contain correctly-worded reminders (with escalating urgency), and the auto-close logic should have updated the 8-day-old item to Status = Expired with a note in the Notes field.


    Common Mistakes & Troubleshooting

    Problem: The Cancel an approval action fails with "Approval not found"

    This happens when the approval was completed (approved or rejected) between when your Get Items query ran and when your Cancel action fires. This is expected behavior — implement a "Configure run after" that also runs on failure, and in the failure handler, simply get the latest SharePoint item status and skip the update if it's already in a terminal state.

    Problem: DaysSinceLastReminder is always 0

    The ticks subtraction works correctly, but if LastReminderSent was set to utcNow() with a string format that doesn't match what ticks() expects, the calculation silently fails. Ensure LastReminderSent is stored as a SharePoint DateTime column, not a text column. Also verify you're not using formatDateTime() when storing — store the raw utcNow() value.

    Problem: Every item in the loop triggers escalation every time the watcher runs

    Check your condition ordering. The auto-close check must be >= the maximum days, the escalation check must be between MaxDaysAllowed and MaxDaysAllowed + 2, and the reminder check must include DaysSinceLastReminder >= 2. If you're missing the DaysSinceLastReminder gate, every watcher run will send a reminder.

    Problem: OOF detection returns false even though the approver is out of office

    The "Get mail tips for a mailbox" action requires appropriate permissions and may return incomplete data if the approver's mailbox is in a different Exchange organization or has restricted external access. Verify the connection account has at minimum Mail.ReadWrite and MailboxSettings.Read permissions. In some tenants, you may need the Microsoft Graph connector instead of the Outlook connector for OOF checks.

    Problem: The watcher flow runs slowly when there are many pending items

    With 200+ items in your loop and multiple actions per item, you can hit Power Automate's action limit for a single run. Break this into batches using a filter that processes items in segments (e.g., by department code), or implement concurrency by setting Apply to each concurrency to 5 parallel iterations. Be careful: if concurrent iterations both try to update the same SharePoint item (edge case), you can get write conflicts.

    Problem: Auto-closed approvals aren't being marked as Expired because the Parallel branch in the originating flow is still waiting

    If you used a Parallel branch with Wait for an approval, that branch will fail when the approval is cancelled — with a "Approval was cancelled" error. This is actually the correct behavior. Configure the Wait for an approval action to run after both success and failure, and in the failure handler, check the SharePoint record status. If it's Expired, send the "request was closed" email to the requestor and exit gracefully.

    Warning

    Don't rely on Power Automate run history as your primary audit log for this system. Run history retains for 28 days (or less depending on your plan). Your SharePoint Notes field is permanent and human-readable. Train your team to look there first when an approver disputes what happened to their request.


    Summary & Next Steps

    You've built a production-grade approval reminder system that goes well beyond "send an email and wait." The key architectural insight is state management: by keeping all approval metadata in SharePoint and using a separate scheduled watcher flow, you get a system that's observable, restartable, and independently testable. Each component has a single responsibility, and failures in one don't cascade into others.

    The specific patterns you can now implement:

    • Tiered reminders with escalating urgency based on ReminderCount and days remaining
    • SLA-based escalation to manager after a configurable deadline, with automatic approval reassignment
    • OOF-aware routing that detects when an approver is unavailable and redirects to a configured delegate
    • Automatic closure of stale requests after a grace period, with full audit trail in SharePoint
    • Admin visibility through SharePoint views and daily digest notifications

    Where to go next:

    If your approval process involves multiple sequential or parallel approvers, the patterns you've built here compose naturally with the multi-stage approval architecture in Designing Multi-Stage Approval Chains in Power Automate: Sequential, Parallel, and Delegated Sign-Off Patterns. Each stage in a sequential chain can have its own MaxDaysAllowed and EscalationLevel tracking, with the watcher flow handling reminders at every stage.

    For complex routing logic — where the right approver depends on department, amount threshold, and request type simultaneously — see Building a Dynamic Multi-Condition Approval Flow in Power Automate: Routing Requests to Different Approvers Based on Form Input, Dollar Thresholds, and Department Rules. The dynamic routing and the reminder/escalation system you've built here are separate concerns that compose cleanly — your watcher doesn't need to know how the approval was routed; it just needs the current ApproverEmail and the SLA from the SharePoint record.

    Finally, if this system will run at organizational scale with hundreds of daily requests, revisit your SharePoint list performance configuration, consider moving to Dataverse for its more robust querying and transaction semantics, and review run history and flow checker techniques at Using Power Automate Run History and Flow Checker to Debug and Fix Failing Flows to build your monitoring practice before you're debugging a production incident at 11 PM.

    Work With Us

    From insight to implementation

    Reading is the start. When you're ready to build the data, automation, or AI systems behind it, our team turns strategy into shipped results.

    Let's Build

    Flow Automation Basics

    Previous

    Automating Microsoft 365 Group Membership Changes with Power Automate: Triggering Flows on Join and Leave Events, Notifying Owners, and Updating SharePoint Records in Real Time

    Related Insights

    Power AutomatePractitioner

    Implementing Exponential Backoff and Circuit Breaker Patterns in Power Automate: Protecting Downstream Systems from Cascading Failures

    24 min
    Power AutomateExpert

    Automating Microsoft 365 Group Membership Changes with Power Automate: Triggering Flows on Join and Leave Events, Notifying Owners, and Updating SharePoint Records in Real Time

    28 min
    Power AutomateFoundation

    Structuring Power Automate Solution Layers for Enterprise ALM: Separating Configuration, Shared Components, and Business Logic Across Managed Solution Tiers

    17 min

    On this page

    • Introduction
    • Prerequisites
    • Understanding the Architecture Before Writing a Single Action
    • Designing Your SharePoint Tracking List
    • Building the Originating Flow: Submit and Track
    • Building the Watcher Flow: Scheduled Reminders with Escalation Logic
    • Step 1: Query Pending Approvals
    • Step 2: Loop and Calculate Age
    • Step 3: The Escalation Decision Tree
    • Detecting Out of Office and Routing to Delegates
    • Building the Completion Flow: Handling Responses
    • Handling the Teams Notification Channel
    • Managing State Transitions and Idempotency
    • The Expression Layer: Working with Dates and Dynamic Content
    • Building an Admin Visibility Dashboard
    • Hands-On Exercise
    • Common Mistakes & Troubleshooting
    • Summary & Next Steps