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.
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 Name | Type | Description |
|---|---|---|
TravelType | Choice (Domestic, International) | Type of trip |
Status | Choice (New, Submitted, ApprovedByManager, RejectedByManager, ApprovedByTravelDesk, RejectedByTravelDesk) | Current step |
StartDate | Date only | First day of travel |
EndDate | Date only | Last day of travel |
Employee | Person (single value) | Who made the request |
Manager | Person (single value) | Direct manager of the employee |
TravelCoordinator | Person (single value) | Travel desk coordinator |
Comments | Multiple lines of text | Justification 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.
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.
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.
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.
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:
- Updates the status to
Submitted. - Submits the form.
- 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.
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:
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:
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.
Set(varStatus, "ApprovedByManager"); SubmitForm(frm_TravelRequest);
In the form's OnSuccess, add a condition to send the next email:
...
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.
Set(varStatus, "RejectedByManager"); SubmitForm(frm_TravelRequest);
Add another condition to OnSuccess to send a rejection email to the employee:
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:
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
Claimsstring 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
LookUpinOnStartwill 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.typeandClaimsproperties. Copy the exact structure from working code. - Links not opening the app: Ensure the
varAppIDincludes the full play URL with?tenantId=.... Test the link separately. - Buttons not visible: Check the
Visibleformula for typos and ensure the form mode isFormMode.View. - Multiple conditions on
OnSuccess: EachIfblock is independent; put them in a logical sequence or use aSwitchto 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)
Learn how small coding practices—like using descriptive names, flattening conditions, and simplifying logic—can make your apps easier to update and less error-prone.
Move past the gallery and discover the hidden patterns for validation, navigation, and smart submission handling in your data entry forms.
PowerShell unlocks admin capabilities that the Power Platform admin center simply doesn’t offer—from recovering deleted apps to blocking trial licenses. Here’s how to wield them safely.