All articles

Your First Approval Flow

A step-by-step guide to building approval flows in Power Automate, including connector provisioning, approval types, custom email content, and handling the response — plus the errors that block most first-time setups.

· 9 min read

Approval flows are one of the top reasons teams adopt Power Automate. Expense approvals, document sign-offs, access requests — the pattern is always the same: someone submits something, someone else approves or rejects it, and the system updates accordingly.

Building one is straightforward, but there are a few things that catch people on their first attempt.

First: Provision the Approvals Connector

Before you can use the Approvals connector, it needs to be provisioned in your environment. This happens automatically the first time someone creates an approval flow, but it can take a few minutes. If you get a “Solution not found” or “Approvals not provisioned” error, here’s what’s happening:

The Approvals connector creates a Dataverse database in your environment (if one doesn’t exist) and installs the Approval solution. This is a one-time setup per environment.

If the provisioning fails:

  1. Make sure you have a Dataverse database in the environment. Go to the Power Platform Admin Center, select the environment, and check if a database exists. If not, create one.
  2. Make sure you have the Environment Maker role or higher.
  3. Try creating a simple test flow with an approval action. Save and run it. The first run often triggers the provisioning.
  4. Wait 5-10 minutes and try again. Provisioning can be slow.

Step-by-Step: A Basic Approval Flow

Step 1: Choose Your Trigger

For this example, we’ll use When a row is added (Dataverse trigger) — the flow starts when someone creates a new Expense record.

Configure the trigger:

  • Table name: Expenses
  • Scope: Organization (processes all new records, regardless of owner)

Step 2: Add the Approval Action

Add Start and wait for an approval from the Approvals connector.

Approval type — this is the important choice:

TypeBehavior
Approve/Reject - First to respondThe first approver to respond decides. Good for “any manager can approve” scenarios.
Approve/Reject - Everyone must approveAll listed approvers must approve. If anyone rejects, the whole request is rejected.
Custom Responses - Wait for one responseYou define custom response options (e.g., “Approve”, “Reject”, “Need More Info”). First response wins.
Custom Responses - Wait for all responsesAll approvers must respond with your custom options.

For most business scenarios, Approve/Reject - First to respond is the right choice.

Step 3: Configure the Approval Details

  • Title: Expense Approval: @{triggerOutputs()?['body/cr5e8_name']}
  • Assigned to: The approver’s email address. Can be dynamic (e.g., from the submitter’s manager field) or a static email. Separate multiple approvers with semicolons.
  • Details: The body of the approval email. Supports HTML.

Making the Approval Email Useful

The Details field supports HTML, so make it informative:

<h3>Expense Request</h3>
<table>
  <tr><td><b>Submitted by:</b></td><td>@{triggerOutputs()?['body/cr5e8_submittedby']}</td></tr>
  <tr><td><b>Amount:</b></td><td>$@{triggerOutputs()?['body/cr5e8_amount']}</td></tr>
  <tr><td><b>Category:</b></td><td>@{triggerOutputs()?['body/cr5e8_category']}</td></tr>
  <tr><td><b>Description:</b></td><td>@{triggerOutputs()?['body/cr5e8_description']}</td></tr>
</table>
<p><a href="https://your-app-url.com/expenses/@{triggerOutputs()?['body/cr5e8_expenseid']}">View in app</a></p>

Adding a link to the source record or app is a small thing that makes a big difference. Approvers can review the full details before deciding.

Step 4: Handle the Response

After the approval action, add a Condition to check the outcome:

outputs('Start_and_wait_for_an_approval')?['body/outcome']

is equal to Approve.

True branch (Approved):

  • Update the Expense record: set Status to “Approved”
  • Send a confirmation email to the submitter
  • Optionally trigger the next step (payment processing, etc.)

False branch (Rejected):

  • Update the Expense record: set Status to “Rejected”
  • Send a rejection email with the approver’s comments

Getting the Approver’s Comments

The approval response includes comments from the approver:

outputs('Start_and_wait_for_an_approval')?['body/responses'][0]?['comments']

Or in the expression editor:

first(outputs('Start_and_wait_for_an_approval')?['body/responses'])?['comments']

Include this in your notification emails so the submitter knows why their request was approved or rejected.

Multi-Level Approvals

For scenarios where Level 1 approves first, then Level 2:

  1. First approval action: Assigned to the direct manager
  2. Condition: If approved, continue. If rejected, stop.
  3. Second approval action: Assigned to the department head
  4. Condition: Handle the second-level response

Each approval action blocks until it gets a response, so the flow naturally handles the sequential nature.

Setting Approval Timeouts

By default, approval flows wait indefinitely. To add a timeout:

  1. Click the menu on the approval action
  2. Select Settings
  3. Set the Timeout duration using ISO 8601 format: P7D (7 days), PT4H (4 hours), P30D (30 days)

When the timeout expires, the action fails. Add error handling (Configure Run After → has timed out) to handle this — usually by rejecting the request automatically and notifying the submitter.

Where Users Respond to Approvals

Approvers can respond from:

  • Email — The approval email includes Approve/Reject buttons directly in Outlook
  • Teams — If Teams approvals are enabled, notifications appear in the Approvals app in Teams
  • Power Automate portal — The Action Items section shows pending approvals
  • Power Automate mobile app — Push notifications for pending approvals

You don’t need to build any custom UI for the approver experience. It works out of the box.

Common Mistakes and How to Fix Them

”The Approvals connection was not found” or “Approvals not provisioned”

The Approvals solution hasn’t been installed in this environment yet. See the provisioning section at the top of this article. The most common fix: make sure the environment has a Dataverse database, then create and run a simple test approval flow to trigger provisioning.

Approval Email Never Arrives

Check:

  1. The Assigned to field has a valid email address (not a display name, not a GUID)
  2. The approver’s mailbox isn’t full
  3. The email isn’t in the spam/junk folder
  4. If using a shared mailbox as the approver, shared mailboxes can’t respond to approvals — use individual user emails

Flow Runs Forever (Never Gets a Response)

The “Start and wait for an approval” action blocks until someone responds. If nobody responds, the flow sits in “Running” state indefinitely. Add a timeout (see above) and handle the timeout path.

Multiple Approvers But Only One Response Needed

You selected “Everyone must approve” when you wanted “First to respond.” Check the Approval type. With “First to respond,” the first person to click Approve or Reject ends the approval.

The Approval Shows Old/Wrong Data

The approval email content is frozen at the time the flow runs. If someone edits the expense record after the flow triggers but before the approver responds, the approval email still shows the original data. If this is a concern, include a link to the live record so the approver always sees current data.

Comments Are Blank Even Though the Approver Typed Something

The comments are optional — approvers can click Approve without typing anything. If you need comments (especially for rejections), mention it in the Details field: “Please provide a reason if rejecting.”

There’s no built-in way to make comments mandatory, but you can use Custom Responses and add a follow-up step that checks for empty comments and sends a reminder.

Share this article LinkedIn X / Twitter

Related articles

Power Automate Formatting Cheat Sheet

formatDateTime, convertTimeZone, formatNumber — these expressions handle 90% of formatting needs in Power Automate. Here's the syntax, common format strings, and fixes for the errors that come up.

· 9 min read