---
title: "Automate Contract Renewal Reminders Before They Auto-Renew"
description: "Build a Power Automate renewal calendar that warns contract owners 90, 30, and 7 days before the notice deadline and escalates when nobody answers."
canonical: "https://adamtheautomator.com/automate-contract-renewal-reminders/"
---

# Automate Contract Renewal Reminders Before They Auto-Renew

> Build a Power Automate renewal calendar that warns contract owners 90, 30, and 7 days before the notice deadline and escalates when nobody answers.

Source: https://adamtheautomator.com/automate-contract-renewal-reminders/

---

ATA Learning

Tap to hide

[

ATA Learning

](/)

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Search for:  

*   [](https://twitter.com/adbertram)
*   [](https://github.com/Adam-the-Automator)
*   [](https://www.linkedin.com/company/adam-the-automator-llc)
*   [](/feed/)

![Automate Contract Renewal Reminders Before They Auto-Renew](https://adamtheautomator.com/wp-content/uploads/publisher/4165d9c85b2b81b6b0e0cc29388883e7/81f5f4ced3359a4de033c21b8c387a3d1b7da4504939a6b068fbf04dfa9681f0.webp)

# Automate Contract Renewal Reminders Before They Auto-Renew

[![](https://secure.gravatar.com/avatar/d0b9d42e21e5622713f8b693aa5c0f9244d5f7dd200ed29b8398f52dee5de337?s=192&d=mm&r=g)Adam Bertram](https://adamtheautomator.com/author/adam-bertram/)13 November 202613 min. read

Categories: [Business Process Automation](/category/business-process-automation/)

Tags:[Power Automate](/tag/power-automate/)[SharePoint](/tag/sharepoint/)[Microsoft Office 365](/tag/microsoft-office-365/)[Automation](/tag/automation/)

Table of Contents

*   [Prerequisites](#prerequisites)
*   [Why the Notice Deadline Beats the Expiry Date](#why-the-notice-deadline-beats-the-expiry-date)
*   [Why a Sweep Beats a Waiting Approval](#why-a-sweep-beats-a-waiting-approval)
*   [Set Up the Contract Renewal Register](#set-up-the-contract-renewal-register)
*   [Three Deadlines per Contract](#three-deadlines-per-contract)
*   [A Log That Outlives the Run History](#a-log-that-outlives-the-run-history)
*   [Build the Daily Sweep](#build-the-daily-sweep)
*   [Calculate Days Left Without an Off-by-One](#calculate-days-left-without-an-off-by-one)
*   [Fire Each Tier Exactly Once](#fire-each-tier-exactly-once)
*   [Why the Stored Stage Is an Ordinal](#why-the-stored-stage-is-an-ordinal)
*   [Send, Acknowledge, Escalate](#send-acknowledge-escalate)
*   [The Escalation Ladder](#the-escalation-ladder)
*   [Test the Ladder, Then Diagnose It](#test-the-ladder-then-diagnose-it)
*   [When the Flow Runs Green and Sends Nothing](#when-the-flow-runs-green-and-sends-nothing)
*   [Where the Calendar Stops Being Enough](#where-the-calendar-stops-being-enough)
*   [The Three Numbers That Show It Works](#the-three-numbers-that-show-it-works)

Open the register where your vendor contracts live and find the last date you can cancel each one without penalty. If that column is missing, the date you have instead is the term end, and the space between the two is where renewals get decided without you.

Close that space with a SharePoint list that carries the dates and one scheduled Power Automate flow that reads it each morning. Then work through day arithmetic that survives time zones, a 90/30/7 ladder that fires each tier once, and an escalation that reaches a second person when the first ignores all three notices.

## Prerequisites

If you want to follow along hands-on, you’ll need:

*   A SharePoint site where you can create a list, with its regional time zone set to the flow’s (Site settings → Regional settings). Contribute permission on the list is enough.
    
*   A Power Automate plan covering the SharePoint and Office 365 Outlook connectors. Both are Standard, and the [types of Power Automate licenses](https://learn.microsoft.com/en-us/power-platform/admin/power-automate-licensing/types) page lists which plans include them.
    
*   The [Approvals connector](https://adamtheautomator.com/power-automate-expense-approval-flow/) only if you want a written decision record at the 30-day tier. It is Standard too.
    
*   One contract owner and one escalation contact per vendor, each with an active mailbox. A reminder to a disabled account is the most common reason a working flow produces nothing you can see.
    

Everything below was built in the Power Automate cloud flow designer in a Microsoft 365 tenant in November 2026, using the SharePoint Online, Office 365 Outlook and Approvals connectors at their Standard tier, with no preview features, and every expression was checked against the workflow expression reference.

## Why the Notice Deadline Beats the Expiry Date

A contract carries several dates, and only one decides whether you keep the deal you think you have.

| Date on the contract | What it decides | Where it lives |
| --- | --- | --- |
| Term end | When the current term runs out | Finance, because the invoice lands there |
| Cancellation deadline | Last day to serve notice that you will not renew | The renewal or evergreen clause (the clause that rolls the term over) |
| Price increase notice | Last day to object to an uplift | The pricing schedule |
| Renewal acknowledgement | Last day to confirm you intend to continue | Sometimes absent |

A twelve-month term with a 60-day notice period puts the decision in month ten. Watch the term end alone and the renewal surfaces in month twelve, when the only choices are to accept it or argue with a vendor who already has you for another year.

### Why a Sweep Beats a Waiting Approval

That timing rules out the design most people reach for: a flow that watches 90 days out and waits for a human. The [approvals known issues](https://learn.microsoft.com/en-us/power-automate/approvals-known-issues) page states: “An approval flow can wait for 28 days. If the wait time exceeds 28 days, that flow fails.” The [flow limits](https://learn.microsoft.com/en-us/power-automate/limits-and-config) page caps a single run at 30 days, pending steps included.

So the calendar becomes a daily sweep that reads the register and acts only on what is due today. The timeline below puts one contract’s dates on a single line, so the run from price increase notice through term end is visible in one look.

![Contract renewal timeline](https://adamtheautomator.com/wp-content/uploads/publisher/bd6dbb76ef9de69bb9ed8f6559d8442a0ef33f8e998252834412a283667eaaeb.jpg)

## Set Up the Contract Renewal Register

One row per deadline, one column per fact the flow needs. A contract with a cancellation deadline and a price increase notice gets two rows, which keeps every expression below pointed at a single date column.

| Column | Type | Why the flow needs it |
| --- | --- | --- |
| Contract and deadline | Single line of text (the list Title) | Names the contract and the deadline in every email |
| Deadline type | Choice: Cancellation, Price increase, Term end | Names the deadline in every subject line, so one flow serves all three |
| Deadline date | Date, Include time set to No | The date the ladder counts down to |
| Owner | Person | Who receives the notice and owns the decision |
| Backup owner | Person | Copied from tier two, and the first escalation target |
| Escalation contact | Person | The manager or budget holder who gets the overdue mail |
| Finance | Person | The second recipient on the overdue mail, copied beside the escalation contact |
| Reminder stage | Number, default 0 | The idempotency marker |
| Last notice sent | Date and time | Drives the nudge without a Delay action |
| Ack status | Choice: Pending, Acknowledged, Escalated, Closed, default Pending | The acknowledgement record |
| Ack by, Ack on, Escalated on | Person, Date, Date | Who answered, and when the second person was pulled in |

Set Include time to No on every date column, so SharePoint stores a calendar date and the comparison against a `yyyy-MM-dd` string stops depending on the hour. The [date and time formatting](https://learn.microsoft.com/en-us/power-automate/date-time-values) reference covers the format strings used below. Set Ack status to Pending on every existing row before the first run; the default covers only new rows.

Do not make the deadline a calculated column: the flow cannot write one, and cannot override it for the vendor whose window is 45 days. Check internal names too: SharePoint keeps the name a column was created with, so a column relabelled “Deadline” still arrives as `NoticeDeadline` and a filter on the label returns nothing.

### Three Deadlines per Contract

One date per contract breaks the first time a price increase lands before the cancellation window opens. Give each deadline its own row, so the stage and last-sent values are tracked separately and one marker cannot cover three ladders. Past four or five deadlines the rows get long to read, the cost of one date column and one ladder.

### A Log That Outlives the Run History

Add a second list, `Reminder log`, with four columns; `Create item` appends one row per notice, outliving the run history.

| Column | Type | Internal name |
| --- | --- | --- |
| Contract | Single line of text (list Title) | `Title` |
| Stage | Number | `Stage` |
| Recipient | Single line of text | `Recipient` |
| Sent at | Date and time | `Sent_x0020_at` |

## Build the Daily Sweep

Create a blank cloud flow with the Recurrence trigger, then set the schedule Microsoft’s [scheduled flow walkthrough](https://learn.microsoft.com/en-us/power-automate/run-scheduled-tasks) describes: Frequency **Day**, Interval **1**, Time zone set to the Windows name of the zone your team works in, Start time 6:00 a.m. on the first morning you want the sweep to run. Six in the morning local leaves a full working day to act. This build uses `Eastern Standard Time`; put your own zone name in that field and in both `convertFromUtc` calls below.

Add a Compose action named `Compose_Window_End` before the loop:

```text
formatDateTime(addDays(utcNow(), 90), 'yyyy-MM-dd')
```

Then **SharePoint: Get items** against the list, with the advanced parameters set:

*   **Filter Query**: `Deadline_x0020_date le '@{outputs('Compose_Window_End')}' and Ack_x0020_status ne 'Closed'`
    
*   **Order By**: `Deadline_x0020_date asc`
    
*   **Top Count**: `5000`
    

Server-side filtering is the difference between a dozen rows and the whole register. Microsoft’s [guidance on the Get items action](https://learn.microsoft.com/en-us/sharepoint/dev/business-apps/power-automate/guidance/working-with-get-items-and-get-files) states the default: “The default item limit is 100 and items are paginated by default as well,” and going past the ceiling means “Power Automate fails and generates an error dialog.” Compare the item count against the row count of the list, because a sweep reading 100 of 400 rows looks healthy in the history.

Add an **Apply to each** over the `value` array; the tier logic below runs inside it. `Compose_Window_End` and `Compose_Today` sit above the loop, and the next section builds the second.

## Calculate Days Left Without an Off-by-One

Power Automate works in UTC, and a date-only SharePoint column stores the site’s local midnight as UTC, so a site east of UTC serialises the previous day. Subtract two dates naively and reminders land a day early or late, a defect nobody notices until a deadline passes.

`dateDifference` will “Return the difference between two dates as a timespan,” per the [workflow expression reference](https://learn.microsoft.com/en-us/azure/logic-apps/expression-functions-reference), and a timespan is not a day count, so comparing it to `90` does not do what it looks like. Ticks are the integer route: “the number of ticks, which are 100-nanosecond intervals, since January 1, 0001 12:00:00 midnight,” with 864,000,000,000 of them in a day.

Add `Compose_Today` outside the loop, before Apply to each:

```text
convertFromUtc(utcNow(), 'Eastern Standard Time', 'yyyy-MM-dd')
```

Then `Compose_DaysLeft` inside it:

```text
div(sub(ticks(convertFromUtc(items('Apply_to_each')?['Deadline_x0020_date'], 'Eastern Standard Time', 'yyyy-MM-dd')),
        ticks(outputs('Compose_Today'))),
    864000000000)
```

The expression converts the stored value into the flow’s zone, so it returns the date the reader typed whenever the site’s regional time zone matches the trigger’s zone. `utcNow()` really is a UTC instant, which makes `Compose_Today` the one place a zone belongs. `ticks` turns both dates into whole numbers and `div` returns whole days: the deadline day reports 0 and the day after reports -1, so `less(DaysLeft, 0)` fires the morning after.

## Fire Each Tier Exactly Once

Three conditions testing `DaysLeft = 90`, `= 30` and `= 7` break the first time the flow is off a day or a contract arrives already 12 days out. Equality against a moving number is a reminder that never sends; a band plus one stored stage survives both.

| Target stage | Tier | Days left | What happens |
| --- | --- | --- | --- |
| 0 | None | 91 or more | Nothing |
| 1 | 90-day | 31 to 90 | First notice to the owner |
| 2 | 30-day | 8 to 30 | Second notice, backup copied, decision requested |
| 3 | 7-day | 0 to 7 | Final notice, escalation contact copied |
| 4 | Overdue | Below 0 | Escalation contact and finance, stage 4 stored |

`Compose_Target_Stage` maps the day count onto that ladder. Put this expression in the Compose action:

```text
if(less(outputs('Compose_DaysLeft'), 0), 4,
  if(lessOrEquals(outputs('Compose_DaysLeft'), 7), 3,
    if(lessOrEquals(outputs('Compose_DaysLeft'), 30), 2,
      if(lessOrEquals(outputs('Compose_DaysLeft'), 90), 1, 0))))
```

A Condition decides whether anything is due. It compares the target against the stored stage, unless Ack status reads Acknowledged. Put this expression in the Condition’s advanced mode:

```text
and(greater(outputs('Compose_Target_Stage'),
    coalesce(items('Apply_to_each')?['Reminder_x0020_stage'], 0)),
  not(equals(items('Apply_to_each')?['Ack_x0020_status']?['Value'], 'Acknowledged')))
```

A row that slipped past its deadline sends the overdue notice on that run and stores 4. Acknowledged rows fail that test and send nothing at any day count; an Escalated row still receives the overdue notice and the stage-4 write.

A blank number column arrives as null, and a logical comparison converts null to an empty string, so `coalesce` reads an unset stage as 0.

### Why the Stored Stage Is an Ordinal

Storing the day count looks simpler and produces a worse bug. A contract first seen at 5 days out stores `5`, and its target stage is 3. The comparison is `greater(Target, Stored)`, so the stored count sits above every target and the condition never passes again: one notice, then silence. A stored ordinal moves up as the deadline approaches, which is the direction the comparison expects.

Inside it, a Switch on the target stage sets the subject, the opening line and the recipients, one Send an email action sits outside, and one Update item follows. Every stage gets a case, including stage 4, which addresses the escalation contact and finance. Set **Reminder stage** to the target stage, **Last notice sent** to `utcNow()`, and **Title** to the row’s title. Set the update’s **Run after** to successful only, which Microsoft’s [error handling guidance](https://learn.microsoft.com/en-us/power-automate/guidance/coding-guidelines/error-handling) describes as controlling “what should happen if an action fails, times out, is skipped, or is successful.” A failed send leaves the stage untouched, and tomorrow repeats the notice.

* * *

_**Key Insight: The stage column is the only thing standing between a daily schedule and ninety identical emails. Run the flow twice in one morning; the second run must send nothing.**_

* * *

The diagram below follows one row through tomorrow’s run: the day count picks a band, the stored stage decides whether anything sends, and the update lands only when the target outranks it.

![Stage decision tree](https://adamtheautomator.com/wp-content/uploads/publisher/27096df985b86de1312d6bd2e9e097e1ba6353e1dfb9299a9965ce4787faef46.jpg)

## Send, Acknowledge, Escalate

The email has one job: get a decision recorded. To is the owner, Cc is the backup from tier two onward, and the subject carries the deadline date, the deadline type and the day count in the flow’s zone, so it sorts:

```text
Action by @{convertFromUtc(items('Apply_to_each')?['Deadline_x0020_date'], 'Eastern Standard Time', 'dd MMM yyyy')}: @{items('Apply_to_each')?['Title']} - @{items('Apply_to_each')?['Deadline_x0020_type']?['Value']} (@{outputs('Compose_DaysLeft')} days)
```

The body carries what expires and when, what happens with no action, a link to the row, and the one action you want. The link is the `{Link}` dynamic content, so the reader lands on the item instead of a site home page:

```text
Open the item: @{items('Apply_to_each')?['{Link}']}
Set Ack status to Acknowledged once you have decided, and the ladder stops.
```

Acknowledgement belongs in a column. A reply reading “let me check” is invisible to a flow, cannot be counted, and leaves with the person who wrote it. Where the decision needs a written record, [start an approval](https://learn.microsoft.com/en-us/power-automate/modern-approvals) at the 30-day tier with a five-day window, well inside the 28-day ceiling. An [approval blocks the branch it sits on](https://adamtheautomator.com/stop-copilot-auto-approvals-human-loop/), so holding one open needs a parallel branch to nudge; here the sweep returns tomorrow. The nudge sits outside the Condition that decides what is due and above rung three, and requires that no tier is due this morning. Its Update item sets **Last notice sent** to `utcNow()`, so it repeats 72 hours later at the earliest. The stage test stays below stage 3, so a stage-3 or stage-4 row gets no further nudges.

```text
and(equals(items('Apply_to_each')?['Ack_x0020_status']?['Value'], 'Pending'),
    less(coalesce(items('Apply_to_each')?['Reminder_x0020_stage'], 0), 3),
    lessOrEquals(outputs('Compose_Target_Stage'),
      coalesce(items('Apply_to_each')?['Reminder_x0020_stage'], 0)),
    less(items('Apply_to_each')?['Last_x0020_notice_x0020_sent'], addDays(utcNow(), -2)))
```

Rung three needs its own Condition, placed outside that Condition, after its Update item, so every row reaches it. The nudge repeats the notice to the owner; this branch moves it to the second person. The gate needs the stored stage as well as the Pending test:

```text
and(equals(items('Apply_to_each')?['Ack_x0020_status']?['Value'], 'Pending'),
    equals(coalesce(items('Apply_to_each')?['Reminder_x0020_stage'], 0), 3),
    lessOrEquals(outputs('Compose_DaysLeft'), 2),
    greaterOrEquals(outputs('Compose_DaysLeft'), 0))
```

The stage test makes the rung reachable. A row that walked 90, 30 and 7 days out stored 3 at the seven-day tier, and every later target is also 3, so `greater(Target, Stored)` stays false and the same gate placed inside that Condition never escalates. Given its own Condition, a row still at stage 2 gets the tier-three notice from the Switch and escalates on the first morning it reads stage 3 at two days out. Send the deadline restatement to the escalation contact with the owner copied, then set **Ack status** to `Escalated` and **Escalated on** to `utcNow()`, leaving **Reminder stage** at 3. The lower bound keeps this branch off the rows rung four handles: a row three days overdue still reads 3 from Get items and clears the upper test, so without the bound it gets a second mail that morning. The Pending test sends it once; the write makes the branch false tomorrow. Rung four needs no separate condition: the target expression maps a negative day count to stage 4, the Switch addresses the escalation contact and finance, and the Update item writes **Reminder stage** 4, which outranks every later target so the row goes quiet. Add one action inside that branch to [append a row to `Reminder log`](https://adamtheautomator.com/power-automate-invoice-approvals-teams/) with the contract, stage, recipient and timestamp.

Choice columns arrive as an object with a `Value` property. Compare `.Value` to the text; comparing the object itself is always false and fails silently.

### The Escalation Ladder

| Rung | Fires when | Who receives it | What gets written |
| --- | --- | --- | --- |
| 1 | 90, 30 and 7 days out | Owner, backup copied from tier two | Reminder stage, last notice sent |
| 2 | No tier due, 72 hours after the last notice, still Pending, stage below 3 | Owner again, deadline restated | Last notice sent |
| 3 | 2 days out, stage 3 stored, still Pending | Escalation contact, owner copied | Ack status Escalated, escalated on |
| 4 | First morning below zero days, unless acknowledged | Escalation contact and finance | Reminder stage 4, `Reminder log` row |

Microsoft’s [notes on known approval issues](https://learn.microsoft.com/en-us/power-automate/approvals-known-issues) cover one wrinkle: when an approver responds by email or in the approvals center, the Teams adaptive card does not update, so read the outcome from the approval record.

## Test the Ladder, Then Diagnose It

Run these on a copy of the list and keep the live register out until they pass.

1.  91 days out: no email, stage 0.
    
2.  Same row at 90 days: one notice, stage 1, no nudge.
    
3.  Second run that morning: nothing. This is the duplicate test that matters.
    
4.  Day 29: the 30-day notice alone, stage 2, no repeat of the 90-day mail.
    
5.  A new row 6 days out: one final notice, stage 3, no backfill of wider tiers, no nudge.
    
6.  Three mornings after the 30-day notice, still Pending: one nudge to the owner, nothing else.
    
7.  Same row at 2 days out, stage 3, still Pending: one mail to the escalation contact, owner copied, Ack status Escalated, no second mail that morning.
    
8.  A row at 2 days out holding stage 1, Ack status Acknowledged: no mail, and none thereafter.
    
9.  Ack status Closed on any row: absent from the Get items output.
    
10.  Blank Ack status and a blank Reminder stage on a new row: one notice, no template error.
     
11.  The row from item 7, one day past its deadline: one mail to the escalation contact and finance, stage 4, one `Reminder log` row, and nothing on the next morning’s run.
     

### When the Flow Runs Green and Sends Nothing

| Symptom | Cause | Fix |
| --- | --- | --- |
| Green runs, no mail | Filter matches nothing: wrong internal name or literal | Count the objects in the Get items output |
| Only some rows processed | Top Count left at 100 | Raise it and compare counts |
| Off by one day | Date column keeps a time, or the site’s zone differs from the flow’s | Include time No, check Regional settings |
| Same reminder daily | Update item skipped, stage never advanced | Check its run-after settings and output |
| Condition never true | Choice column compared as an object | Compare `.Value` |

Start in the run history: zero objects from Get items means the filter is at fault. A dozen objects and no email points at the condition or the stage comparison. A hundred where you expected two hundred is Top Count.

Power Automate also suspends flows that fail consistently or never trigger, and the [limits page](https://learn.microsoft.com/en-us/power-automate/limits-and-config) names the recorded reasons, including `AlwaysFailingDetected` and `NeverTriggeringDetected`.

* * *

_**Warning: A disabled owner mailbox does not fail Send an email. The action succeeds, the run goes green, and the message bounces. Check the sender’s non-delivery report before you check the flow.**_

* * *

## Where the Calendar Stops Being Enough

The calendar knows what you typed into it. It cannot read the contract renewal clause, cannot confirm the notice period matches the contract, and cannot negotiate the uplift. Those stay human jobs, moved from month twelve to month ten. The flow itself runs under the account that created its connections, so when that person leaves, the sweep stops with them. Add flow ownership to the offboarding checklist, so the sweep outlives them.

The run history is not an audit trail either. [Runs are retained for 30 days](https://learn.microsoft.com/en-us/power-automate/limits-and-config), shorter than the cycle being tracked, so the evidence of what was sent disappears before the next contract renewal. The overdue rung appends a row to the `Reminder log`; add it to the three reminder branches, one action each.

Volume is the other boundary. A list plus a schedule covers contract expiry tracking for a few hundred deadlines, and the [5,000-item ceiling on a single Get items call](https://learn.microsoft.com/en-us/sharepoint/dev/business-apps/power-automate/guidance/working-with-get-items-and-get-files) is where that approach ends. Departments that need isolation want separate lists rather than item-level permissions, and clause extraction has moved past what a list and a schedule should carry.

A calendar like this is one instance of a pattern covering most recurring back-office paperwork, and [Adam builds and maintains these business process automations](https://adamtheautomator.com/about-adam/) for teams that would rather own the outcome than the flow.

### The Three Numbers That Show It Works

Tomorrow morning, check three numbers: the item count from Get items against the row count of the list, the days-left value on a deadline you know, and the last notice sent timestamp on a contract that should have received a tier. Those three answer whether the calendar works, long before a renewal depends on it.

Share this article

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fadamtheautomator.com%2Fautomate-contract-renewal-reminders%2F&text=Automate%20Contract%20Renewal%20Reminders%20Before%20They%20Auto-Renew)[Share on Facebook](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fadamtheautomator.com%2Fautomate-contract-renewal-reminders%2F)[Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fadamtheautomator.com%2Fautomate-contract-renewal-reminders%2F)

## Related Posts

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b812d96aaea8aa772a1e7/1406d20d337f1ef7937f468af2feb30f4a99e0ac228d4316500d0d14b5868bd7.png)

### [Power Automate: Streamline Invoice Approvals in Teams](/power-automate-invoice-approvals-teams/)

Build a Power Automate flow that routes invoice approvals through Teams, stores documents in SharePoint, and preserves an audit trail.

![](https://adamtheautomator.com/wp-content/uploads/publisher/3ec5d9c85b2b81b19274c73baf4c55f3/98f4af306255355a84e7293721f7eba0ae787ac2055f021d4fddbae4f4385bee.webp)

### [Automate Expense Report Approvals with Power Automate](/power-automate-expense-approval-flow/)

Build a Power Automate approval flow on a SharePoint list that routes expense reports, closes the self-trigger loop, and exports approved spend for finance.

![](https://adamtheautomator.com/wp-content/uploads/2026/07/featured_image-3.webp)

### [Power Automate: Build a Reliable AI Document Intake Pipeline](/power-automate-document-intake-pipeline/)

Build a Power Automate intake pipeline that deduplicates Outlook attachments, extracts fields with AI Builder, and routes low-confidence results to Teams.

## Categories

*   [IT Ops](/category/it-ops/)
*   [Cloud](/category/cloud/)
*   [DevOps](/category/devops/)
*   [Home Ops](/category/home-ops/)
*   [Information Security](/category/infosec/)
*   [Software Development](/category/software-development/)

## Site

*   [Home](/)
*   [Tutorials](/tutorials/)
*   [Instructors](/author/)
*   [Advertising](/advertising/)
*   [Recommended Resources](/resources/)
*   [About Adam](/about-adam/)

Copyright 2026© ATA Learning | [Privacy Policy](/privacy/)
