Tutorials/Power Apps/Managing Multi-Step Approvals in Power Apps: A Practical Guide
Power Appsintermediate

Managing Multi-Step Approvals in Power Apps: A Practical Guide

Create a travel request approval app where submissions are automatically routed to a manager and then a travel desk, with email notifications and clear status tracking.

NA
Narmer Abader
@narmer · Published June 3, 2026

Approval workflows are one of the most common business needs solved with Power Apps. Whether it's time off, equipment, or travel, the pattern is the same: an employee fills in a form, the request is sent to their manager for sign-off, and then to another person (HR, IT, travel desk) for a second approval. The challenge is connecting all the pieces without manual emails or separate systems.

In this article, I'll show you how to build a travel request approval app that uses a SharePoint list for storage, automatically populates the requester's manager and travel desk contact, sends approval emails with direct links to the app, and lets each approver accept or reject the request. The same pattern can be adapted for any multi‑level approval scenario.

Example Scenario: Travel Request Approval

A company needs a simple app for employees to submit travel requests. Each request must be approved by the employee's direct manager and then by a travel desk coordinator. The list tracks every step of the approval chain so everyone knows the current status.

We'll use a SharePoint list called Travel Requests with the following columns.

Column NameTypeDescription
TravelTypeChoice (Domestic, International)Type of trip
StatusChoice (New, Submitted, ApprovedByManager, RejectedByManager, ApprovedByTravelDesk, RejectedByTravelDesk)Current step
StartDateDate onlyFirst day of travel
EndDateDate onlyLast day of travel
EmployeePerson (single value)Who made the request
ManagerPerson (single value)Direct manager of the employee
TravelCoordinatorPerson (single value)Travel desk coordinator
CommentsMultiple lines of textJustification or notes

Building the Canvas App

Start a new blank canvas app in Power Apps Studio. Name it Travel Request App. Connect the app to your Travel Requests SharePoint list.

Add an Edit form to the screen and connect it to the data source. Arrange the fields in a single column for readability. For now, keep all fields visible – we'll control editing later.

Auto‑Populating Hidden Fields

We want the employee to only fill in TravelType, StartDate, EndDate, and Comments. The rest should be set automatically and locked.

Status

The default status should be New for a fresh request, or the existing status when viewing a submitted record.

powerfxDefault property of Status card
If(
  frm_TravelRequest.Mode = FormMode.New,
  {Value: Coalesce(varStatus, "New")},
  ThisItem.Status
)

When the submit button is clicked, we'll change the status to Submitted.

Employee

Use User() to get the current user's details and write them back as a SharePoint person field.

powerfxDefault property of Employee card
If(
  frm_TravelRequest.Mode = FormMode.New,
  {
      '@odata.type': "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
      Claims: "i:0#.f|membership|" & User().Email,
      Department: "",
      DisplayName: User().FullName,
      Email: User().Email,
      JobTitle: "",
      Picture: ""
  },
  ThisItem.Employee
)

Manager

The Office365Users connector makes it easy to look up the manager of the current user. Add that connector to your app.

powerfxDefault property of Manager card
With(
  {
      wManager: Office365Users.ManagerV2(User().Email)
  },
  If(
      frm_TravelRequest.Mode = FormMode.New,
      {
          '@odata.type': "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
          Claims: "i:0#.f|membership|" & wManager.mail,
          Department: "",
          DisplayName: wManager.displayName,
          Email: wManager.mail,
          JobTitle: "",
          Picture: ""
      },
      ThisItem.Manager
  )
)

Travel Coordinator

For the second approver (travel desk), you can either hard‑code a person or look it up from another list. Here we'll hard‑code an example email and display name. In production you'd point to a SharePoint list of roles.

powerfxDefault property of TravelCoordinator card
If(
  frm_TravelRequest.Mode = FormMode.New,
  {
      '@odata.type': "#Microsoft.Azure.Connectors.SharePoint.SPListExpandedUser",
      Claims: "i:0#.f|membership|" & "traveldesk@contoso.com",
      Department: "",
      DisplayName: "Sam Rivera",
      Email: "traveldesk@contoso.com",
      JobTitle: "",
      Picture: ""
  },
  ThisItem.TravelCoordinator
)

Locking Auto‑Filled Fields

Employees should not be able to change the automatically set values. Set the DisplayMode of the following cards to DisplayMode.View:

  • Status
  • Employee
  • Manager
  • TravelCoordinator

This prevents tampering while still showing the values.

Submitting the Request and Sending the First Approval Email

We need a Submit button that:

  1. Updates the status to Submitted.
  2. Submits the form.
  3. Sends an email to the manager with a link to the approval screen.

Before that, store the app's play URL in the OnStart property of the app so you can build links.

powerfxApp OnStart (set once)
Set(
  varAppID,
  "https://apps.powerapps.com/play/e3a1b234-56c7-42d8-90ef-1234567890ab?tenantId=your-tenant-id"
);

// On launch, check if a record id was passed
Set(
  varRequest,
  LookUp('Travel Requests', ID = Value(Param("recordid")))
);

If(
  IsBlank(varRequest),
  NewForm(frm_TravelRequest),
  ViewForm(frm_TravelRequest)
);

Now add the Office365Outlook connector for sending emails. In the form's OnSuccess property, place this logic:

powerfxForm OnSuccess
ViewForm(frm_TravelRequest);
Set(varRequest, frm_TravelRequest.LastSubmit);

If(
  varRequest.Status.Value = "Submitted",
  Office365Outlook.SendEmailV2(
      varRequest.Manager.Email,
      "Travel Request – Approval Required",
      "A " & varRequest.TravelType.Value & " travel request was submitted by " &
      varRequest.Employee.DisplayName & " from " &
      Text(varRequest.StartDate, "mm/dd/yyyy") & " to " &
      Text(varRequest.EndDate, "mm/dd/yyyy") & "." &
      "<br><br><a href='" & varAppID & "?recordid=" & varRequest.ID & "'>Click here</a>" &
      " to approve or reject this request.",
      {Importance: "Normal"}
  )
);

The manager's inbox will receive an email with a direct deep link to the app. When they open it, the app reads the recordid parameter and shows the request in view mode.

The Approval Buttons

Once the manager opens the request, they should see Approve and Reject buttons at the bottom of the screen. These buttons only appear when the request is in "Submitted" status.

Add two buttons and set their Visible property to:

powerfxVisible property for Approve/Reject buttons
frm_TravelRequest.Mode = FormMode.View && varRequest.Status.Value = "Submitted"

Manager Approves

When the manager clicks Approve, update the status and submit the form again. Then send an email to the travel coordinator for the second approval.

powerfxOnSelect of Approve button (manager)
Set(varStatus, "ApprovedByManager");
SubmitForm(frm_TravelRequest);

In the form's OnSuccess, add a condition to send the next email:

powerfxExtend existing OnSuccess
...
If(
  varRequest.Status.Value = "Submitted",
  // send email to manager (already above)
  ...
);

If(
  varRequest.Status.Value = "ApprovedByManager",
  Office365Outlook.SendEmailV2(
      varRequest.TravelCoordinator.Email,
      "Travel Request – Second Approval Needed",
      "A travel request has been approved by " & varRequest.Manager.DisplayName &
      ". Please review and approve or reject.<br><br>" &
      "<a href='" & varAppID & "?recordid=" & varRequest.ID & "'>Open request</a>",
      {Importance: "Normal"}
  )
);

Manager Rejects

If the manager rejects, set the status to RejectedByManager and notify the employee.

powerfxOnSelect of Reject button (manager)
Set(varStatus, "RejectedByManager");
SubmitForm(frm_TravelRequest);

Add another condition to OnSuccess to send a rejection email to the employee:

powerfxRejection email in OnSuccess
If(
  varRequest.Status.Value = "RejectedByManager",
  Office365Outlook.SendEmailV2(
      varRequest.Employee.Email,
      "Travel Request – Not Approved",
      "Your travel request from " & Text(varRequest.StartDate, "mm/dd/yyyy") &
      " has been denied by your manager."
  )
);

Second Approver (Travel Coordinator) Buttons

The travel coordinator will see the request when the status is ApprovedByManager. Add two more buttons with Visible property:

powerfxVisible for travel desk buttons
frm_TravelRequest.Mode = FormMode.View && varRequest.Status.Value = "ApprovedByManager"

Their Approve sets status to ApprovedByTravelDesk, and Reject to RejectedByTravelDesk. On final approval, an email is sent to the employee confirming the request is fully approved.

Security, Performance, and Delegation Notes

  • Person fields: The Claims string must be exactly as shown. Mismatched formats cause SharePoint to reject the write. Always test with a few users.
  • Office365Users.ManagerV2 is not delegation‑safe in a gallery, but used on a single record it performs fine. For large lookups, consider a SharePoint list with manager mappings.
  • Office365Outlook.SendEmailV2 has rate limits (30 messages per minute for most plans). If your app sends many emails, move logic to Power Automate.
  • Delegation warnings: The LookUp in OnStart will break if the list has more than 500 items unless you adjust the SharePoint delegate threshold. For larger lists, use a filter with indexed columns or shift to Power Automate.

Common Mistakes and Troubleshooting

  • Email not sending: The Office365Outlook connector must be added and the user must be signed into the same work/school account that owns the SharePoint site.
  • Person field not populating: Forgetting the @odata.type and Claims properties. Copy the exact structure from working code.
  • Links not opening the app: Ensure the varAppID includes the full play URL with ?tenantId=.... Test the link separately.
  • Buttons not visible: Check the Visible formula for typos and ensure the form mode is FormMode.View.
  • Multiple conditions on OnSuccess: Each If block is independent; put them in a logical sequence or use a Switch to avoid overwriting variables.

Recommendation

This app works well for teams under 100 people and low‑volume approval requests. For larger organizations or more complex workflows (multiple simultaneous approvers, conditional routing, attachments), consider using Power Automate for the approval logic and leaving Power Apps as the front end. The approach shown here remains a solid foundation for many internal business apps.

References

  • Original article by Matthew Devaney: Make A Power Apps Approvals Form
  • Microsoft Learn: Create a canvas app from a SharePoint list (official docs link placeholder)
  • Microsoft Learn: Office365Users connector reference (official docs link placeholder)