Wicked Smart Data
LearnArticlesAbout
Sign InSign Up
LearnArticlesAboutContact
Sign InSign Up
Wicked Smart Data

The go-to platform for professionals who want to master data, automation, and AI — from Excel fundamentals to cutting-edge machine learning.

Platform

  • Learning Paths
  • Articles
  • About
  • Contact

Connect

  • Contact Us
  • RSS Feed

© 2026 Wicked Smart Data. All rights reserved.

Privacy PolicyTerms of Service
All Articles
Building Approval Workflows with Power Automate

Building Approval Workflows with Power Automate

Power Automate🔥 Expert21 min readApr 3, 2026Updated Apr 3, 2026
Table of Contents
  • Prerequisites
  • Understanding Power Automate's Approval Architecture
  • Designing Complex Approval Routing Logic
  • Building Custom Approval Forms and Data Validation
  • Implementing Multi-Stage Sequential and Parallel Approvals
  • Advanced Approver Assignment and Delegation
  • Building Comprehensive Audit Trails and Reporting
  • Handling Timeouts, Escalations, and Exception Scenarios
  • Integration Patterns with External Systems
  • Hands-On Exercise: Building an Advanced Purchase Requisition Workflow
  • Common Mistakes & Troubleshooting

Building Approval Workflows with Power Automate

Picture this: Your organization processes dozens of purchase requests daily, each requiring approval from department managers before moving to finance. Currently, these requests bounce around email threads, get lost in busy inboxes, and create bottlenecks that delay critical business operations. Meanwhile, your finance team has no visibility into pending approvals, and requesters have no idea where their submissions stand in the process.

This scenario plays out across organizations worldwide—approval workflows that should streamline operations instead create chaos. Power Automate's approval system transforms these manual, error-prone processes into structured, auditable workflows that provide visibility, enforce business rules, and adapt to your organization's unique requirements.

By the end of this lesson, you'll architect sophisticated approval workflows that handle complex routing logic, integrate with multiple data sources, and provide comprehensive audit trails. We'll build a real-world purchase requisition system that demonstrates advanced patterns you can adapt to any approval scenario.

What you'll learn:

  • Design multi-stage approval workflows with conditional routing
  • Implement dynamic approver assignment based on business rules
  • Create custom approval forms with data validation and field logic
  • Build comprehensive audit trails and reporting mechanisms
  • Handle approval timeouts, escalations, and delegation scenarios
  • Integrate approvals with external systems like SharePoint, SQL Server, and Teams

Prerequisites

You should have experience creating basic Power Automate flows, understand SharePoint list operations, and be comfortable with Power Automate expressions and functions. Familiarity with JSON structure and basic HTTP concepts will help when we explore advanced integration patterns.

Understanding Power Automate's Approval Architecture

Power Automate's approval system operates on a sophisticated infrastructure that goes far beyond simple yes/no decisions. The system maintains approval metadata, manages state transitions, and provides APIs for custom integrations—but understanding its internal workings is crucial for building robust workflows.

When you create an approval, Power Automate generates a unique approval ID and stores metadata in Microsoft's Common Data Service. This metadata includes approval status, assigned approvers, response history, and custom properties you define. The system automatically handles approval notifications through multiple channels (email, Teams, mobile app) and maintains a persistent approval center where users can review pending requests.

The approval object itself contains several critical properties that influence workflow behavior:

{
  "id": "approval-guid-here",
  "title": "Purchase Request - $15,000 Software License",
  "details": {
    "requestor": "john.smith@company.com",
    "amount": 15000,
    "department": "IT",
    "vendor": "Microsoft Corporation"
  },
  "status": "Pending",
  "approvers": ["manager@company.com"],
  "responses": [],
  "createdTime": "2024-01-15T10:30:00Z",
  "completedTime": null
}

The status field drives workflow logic and can contain values like "Pending," "Approved," "Rejected," or "Canceled." Understanding these states is crucial because approval conditions evaluate differently based on current status, and parallel approvals require all approvers to respond before the workflow continues.

Designing Complex Approval Routing Logic

Real-world approval workflows rarely follow simple linear paths. Purchase requests might require different approvers based on amount thresholds, department policies, or vendor relationships. Let's build a sophisticated routing engine that handles these complexities.

Consider a purchase requisition system with these business rules:

  • Amounts under $1,000: Department manager approval only
  • Amounts $1,000-$10,000: Department manager + Finance approval
  • Amounts over $10,000: Department manager + Finance + VP approval
  • IT purchases over $5,000: Additional IT Security review
  • Recurring vendor purchases: Skip vendor verification step

Here's how we implement this logic using Power Automate's condition and switch controls:

Initialize variable: ApprovalStage (String) = "Department"
Initialize variable: RequiredApprovers (Array) = []

Switch on: triggerOutputs()?['body/Amount']
  Case 1: less(triggerOutputs()?['body/Amount'], 1000)
    Append to array: RequiredApprovers
      Value: triggerOutputs()?['body/DepartmentManager']
  
  Case 2: and(
    greaterOrEquals(triggerOutputs()?['body/Amount'], 1000),
    less(triggerOutputs()?['body/Amount'], 10000)
  )
    Append to array: RequiredApprovers
      Value: [
        triggerOutputs()?['body/DepartmentManager'],
        "finance-approver@company.com"
      ]
  
  Case 3: greaterOrEquals(triggerOutputs()?['body/Amount'], 10000)
    Append to array: RequiredApprovers
      Value: [
        triggerOutputs()?['body/DepartmentManager'],
        "finance-approver@company.com",
        "vp-operations@company.com"
      ]

This approach scales better than nested conditions because it's easier to modify approval rules without restructuring the entire workflow. However, we need additional logic to handle cross-cutting concerns like IT security reviews:

Condition: and(
  equals(triggerOutputs()?['body/Category'], 'IT'),
  greater(triggerOutputs()?['body/Amount'], 5000)
)
  If yes:
    Append to array: RequiredApprovers
      Value: "it-security@company.com"

The real power emerges when we make approver assignment dynamic. Instead of hardcoding email addresses, we can query organizational data to find the appropriate approvers:

Get department manager:
  HTTP Request to: /_api/web/lists/getbytitle('Department Directory')/items
  Filter: DepartmentName eq '@{triggerOutputs()?['body/Department']}'
  Select: ManagerEmail

Get finance approver by region:
  HTTP Request to: /_api/web/lists/getbytitle('Finance Teams')/items
  Filter: and(
    Region eq '@{triggerOutputs()?['body/RequestorRegion']}',
    Active eq true
  )
  Select: ApproverEmail

This pattern ensures approval routing remains accurate even as organizational structure changes, and it enables sophisticated rules like regional finance approvers or department-specific procurement policies.

Building Custom Approval Forms and Data Validation

Power Automate's default approval interface works for simple scenarios, but enterprise workflows require custom forms with validation rules, conditional fields, and rich data presentation. We'll build a purchase requisition form that adapts based on user input and enforces business rules.

The approval details section accepts JSON-formatted data, giving us complete control over form layout and content. Here's a sophisticated approach that creates a dynamic form structure:

{
  "requestDetails": {
    "requestId": "PR-2024-0156",
    "requestor": {
      "name": "John Smith",
      "email": "john.smith@company.com",
      "department": "IT Infrastructure",
      "costCenter": "CC-4421"
    },
    "purchase": {
      "description": "Microsoft Office 365 E5 licenses for development team",
      "justification": "Required for advanced compliance features and Power BI Pro access",
      "amount": 15840.00,
      "currency": "USD",
      "vendor": {
        "name": "Microsoft Corporation",
        "approved": true,
        "contract": "MSA-2023-001"
      },
      "category": "Software License",
      "urgent": false,
      "recurring": true,
      "renewalDate": "2025-01-15"
    },
    "approvalHistory": [],
    "attachments": [
      {
        "name": "Quote-MS-O365-2024.pdf",
        "url": "https://company.sharepoint.com/sites/procurement/documents/Quote-MS-O365-2024.pdf"
      }
    ]
  },
  "formConfig": {
    "showBudgetImpact": true,
    "requireJustification": true,
    "allowCounterOffer": false
  }
}

This structure enables rich form rendering while maintaining data integrity. The approver sees formatted information instead of raw field values, and the formConfig section controls which form elements appear based on approval context.

For advanced validation, we can implement server-side checks before creating the approval. This prevents invalid requests from entering the workflow:

Validate purchase request:
  Condition: Budget validation
    Expression: greater(
      sub(
        body('Get_department_budget')?['AnnualBudget'],
        body('Get_department_spending')?['YTDSpending']
      ),
      triggerOutputs()?['body/Amount']
    )
  
  If budget insufficient:
    Send notification to requestor
    Terminate workflow
  
  Condition: Vendor validation
    Expression: contains(
      body('Get_approved_vendors')?['value'],
      triggerOutputs()?['body/VendorName']
    )
  
  If vendor not approved:
    Create approval: Vendor approval required
    Wait for approval response
    If vendor approval rejected:
      Terminate workflow

This validation layer ensures that only legitimate requests reach approvers, reducing noise and preventing policy violations from propagating through the approval chain.

Implementing Multi-Stage Sequential and Parallel Approvals

Enterprise approval workflows often require both sequential stages (where one approval must complete before the next begins) and parallel approvals (where multiple approvers can respond simultaneously). Power Automate handles both patterns, but combining them requires careful orchestration.

Let's implement a complex workflow where:

  1. Department manager must approve first (sequential)
  2. Finance and IT Security approve in parallel (parallel)
  3. VP provides final approval if amount exceeds threshold (conditional sequential)

Here's the sequential portion:

Stage 1 - Department Manager Approval:
  Create an approval:
    Title: Purchase Request Approval - Stage 1 of 3
    Assigned to: @{variables('DepartmentManager')}
    Details: @{variables('ApprovalDetails')}
    
  Wait for an approval:
    Approval ID: @{body('Create_Stage1_approval')?['name']}
    
  Switch on approval outcome:
    Case: Approve
      Update SharePoint item: Status = "Department Approved"
      Continue to next stage
    Case: Reject
      Update SharePoint item: Status = "Rejected by Department"
      Send rejection notification
      Terminate workflow

The parallel approval stage requires more sophisticated handling because we must wait for all approvers to respond before proceeding:

Stage 2 - Parallel Finance and IT Security Approval:
  Create Finance approval:
    Title: Purchase Request Approval - Finance Review
    Assigned to: @{variables('FinanceApprover')}
    Details: @{variables('ApprovalDetails')}
    
  Create IT Security approval:
    Title: Purchase Request Approval - Security Review
    Assigned to: @{variables('ITSecurityApprover')}
    Details: @{variables('ApprovalDetails')}
    
  Wait for Finance approval:
    Approval ID: @{body('Create_Finance_approval')?['name']}
    
  Wait for IT Security approval:
    Approval ID: @{body('Create_ITSecurity_approval')?['name']}
    
  Condition: Both approvals successful
    Expression: and(
      equals(body('Wait_for_Finance_approval')?['outcome'], 'Approve'),
      equals(body('Wait_for_ITSecurity_approval')?['outcome'], 'Approve')
    )

This approach works but has a critical limitation: if one approver rejects the request, the workflow still waits for the other approver to respond. For better user experience, we need early termination logic:

Apply to each: Parallel approvals array
  Switch on approval outcome:
    Case: Approve
      Update tracking variable: ApprovedCount + 1
    Case: Reject
      Update SharePoint item: Status = "Rejected"
      Cancel remaining approvals
      Terminate workflow
      
  Condition: All parallel approvals complete
    Expression: equals(variables('ApprovedCount'), length(variables('ParallelApprovers')))
    If yes: Continue to next stage

The final sequential stage demonstrates conditional approval routing:

Stage 3 - VP Approval (if required):
  Condition: Amount exceeds VP threshold
    Expression: greater(triggerOutputs()?['body/Amount'], 10000)
    
  If yes:
    Create VP approval:
      Title: Purchase Request Approval - Executive Review
      Assigned to: @{variables('VPApprover')}
      Details: @{add(
        variables('ApprovalDetails'),
        json(concat(
          '{"previousApprovals":',
          string(variables('ApprovalHistory')),
          '}'
        ))
      )}
      
    Wait for VP approval:
      Approval ID: @{body('Create_VP_approval')?['name']}

This pattern scales to any number of sequential and parallel stages. The key is maintaining clear state variables that track approval progress and enable proper error handling at each stage.

Advanced Approver Assignment and Delegation

Static approver assignment breaks down in dynamic organizations where people change roles, take vacations, or delegate authority. Power Automate provides built-in delegation support, but implementing robust approver assignment requires additional logic.

The system automatically handles delegation when users set up delegates in their Outlook settings, but we need fallback mechanisms for when primary approvers are unavailable. Here's a comprehensive approach:

Dynamic Approver Resolution:
  Get primary approver:
    HTTP Request: /_api/web/siteusers/getbyemail('@{variables('PrimaryApprover')}')?$select=Id,LoginName,Title
    
  Check approver availability:
    HTTP Request: /api/v1.0/users/@{variables('PrimaryApprover')}/presence
    
  Condition: Approver unavailable or out of office
    If yes:
      Get backup approver:
        HTTP Request: /_api/web/lists/getbytitle('Approver Matrix')/items
        Filter: and(
          PrimaryApprover eq '@{variables('PrimaryApprover')}',
          Active eq true
        )
        Select: BackupApprover
        
      Condition: Backup approver available
        If yes: Assign to backup
        If no: Escalate to department head

For complex organizational hierarchies, we can implement recursive approver lookup:

Get Manager Chain:
  Initialize variable: ManagerChain (Array) = []
  Initialize variable: CurrentUser (String) = @{triggerOutputs()?['body/RequestorEmail']}
  
  Do until: equals(variables('CurrentUser'), variables('UltimateApprover'))
    Get user manager:
      HTTP Request: /api/v1.0/users/@{variables('CurrentUser')}/manager
      
    Append to ManagerChain: @{body('Get_user_manager')?['mail']}
    Set CurrentUser: @{body('Get_user_manager')?['mail']}
    
    Condition: Manager found
      If no: Break from loop

This creates a complete management chain that we can use for escalation policies or multi-level approval requirements. We can then select the appropriate approver based on business rules:

Select Approver by Authority Level:
  Switch on required authority level:
    Case: "Budget": 
      Filter ManagerChain where BudgetAuthority >= @{triggerOutputs()?['body/Amount']}
      Select first result
    Case: "Technical":
      Filter ManagerChain where TechnicalAuthority eq true
      Select first result
    Case: "Procurement":
      Get procurement approver for category: @{triggerOutputs()?['body/Category']}

This approach ensures that approvals always reach someone with appropriate authority, even in complex organizational structures or when specific approvers are unavailable.

Building Comprehensive Audit Trails and Reporting

Approval workflows generate significant business data that organizations need for compliance, process improvement, and operational insights. Power Automate captures basic approval metadata, but building comprehensive audit trails requires additional instrumentation.

Let's create a robust auditing system that captures every interaction:

Initialize Audit Log:
  Create SharePoint item in Approval Audit list:
    RequestId: @{variables('RequestId')}
    Action: "Workflow Started"
    Actor: @{triggerOutputs()?['body/RequestorEmail']}
    Timestamp: @{utcNow()}
    Details: {
      "originalRequest": @{triggerOutputs()?['body']},
      "workflowVersion": "2.1",
      "triggeredBy": "SharePoint List Item Creation"
    }

For each approval stage, we log detailed information:

Log Approval Stage:
  Update SharePoint item in Approval Audit list:
    RequestId: @{variables('RequestId')}
    Action: "Approval Created"
    Actor: "System"
    Timestamp: @{utcNow()}
    Details: {
      "approvalId": @{body('Create_approval')?['name']},
      "assignedTo": @{variables('CurrentApprover')},
      "stage": @{variables('CurrentStage')},
      "expectedResponseTime": @{addHours(utcNow(), 24)},
      "escalationPolicy": @{variables('EscalationPolicy')}
    }

When approvers respond, we capture rich context:

Log Approval Response:
  Create SharePoint item in Approval Audit list:
    RequestId: @{variables('RequestId')}
    Action: @{concat('Approval ', body('Wait_for_approval')?['outcome'])}
    Actor: @{body('Wait_for_approval')?['responses'][0]['approver']}
    Timestamp: @{body('Wait_for_approval')?['responses'][0]['submissionTime']}
    Details: {
      "approvalId": @{body('Wait_for_approval')?['name']},
      "outcome": @{body('Wait_for_approval')?['outcome']},
      "comments": @{body('Wait_for_approval')?['responses'][0]['comments']},
      "responseTime": @{div(
        sub(
          ticks(body('Wait_for_approval')?['responses'][0]['submissionTime']),
          ticks(body('Create_approval')?['createdTime'])
        ),
        600000000
      )},
      "deviceInfo": @{body('Wait_for_approval')?['responses'][0]['source']}
    }

This audit trail enables sophisticated reporting. We can build Power BI dashboards that show:

  • Average approval times by department and category
  • Approval success rates and common rejection reasons
  • Bottlenecks in the approval process
  • Compliance with SLA targets
  • Approver workload distribution

For real-time monitoring, we can send metrics to Azure Application Insights:

Send Telemetry:
  HTTP POST to Application Insights:
    Headers: 
      Content-Type: application/json
    Body: {
      "name": "Microsoft.ApplicationInsights.Event",
      "time": "@{utcNow()}",
      "data": {
        "baseType": "EventData",
        "baseData": {
          "name": "ApprovalWorkflow.StageCompleted",
          "properties": {
            "requestId": "@{variables('RequestId')}",
            "stage": "@{variables('CurrentStage')}",
            "outcome": "@{variables('StageOutcome')}",
            "processingTimeMinutes": "@{variables('ProcessingTime')}",
            "approverType": "@{variables('ApproverType')}"
          },
          "measurements": {
            "amount": @{triggerOutputs()?['body/Amount']},
            "responseTimeHours": @{variables('ResponseTimeHours')}
          }
        }
      }
    }

This telemetry feeds into operational dashboards that help identify process improvements and ensure SLA compliance.

Handling Timeouts, Escalations, and Exception Scenarios

Real-world approval workflows must handle scenarios where approvers don't respond within expected timeframes, are unavailable, or where business conditions change during the approval process. Power Automate provides timeout capabilities, but robust exception handling requires additional logic.

Let's implement a comprehensive escalation system:

Create Approval with Timeout:
  Create an approval:
    Title: @{variables('ApprovalTitle')}
    Assigned to: @{variables('PrimaryApprover')}
    Details: @{variables('ApprovalDetails')}
    
  Wait for approval with timeout:
    Approval ID: @{body('Create_approval')?['name']}
    Timeout: 24 hours
    
  Switch on wait outcome:
    Case: "TimedOut"
      Log timeout event
      Initiate escalation procedure
      
    Case: "Approved"
      Continue workflow
      
    Case: "Rejected" 
      Handle rejection logic
      
    Default:
      Log unexpected outcome
      Send alert to administrators

The escalation procedure implements a multi-tier strategy:

Escalation Procedure:
  Initialize variable: EscalationLevel (Integer) = 1
  Initialize variable: MaxEscalations (Integer) = 3
  
  Do until: or(
    equals(variables('ApprovalReceived'), true),
    greater(variables('EscalationLevel'), variables('MaxEscalations'))
  )
  
    Switch on EscalationLevel:
      Case 1: Send reminder to original approver
        Send reminder email
        Wait 4 hours
        
      Case 2: Notify backup approver
        Get backup approver from directory
        Create new approval for backup approver
        Wait 12 hours
        
      Case 3: Escalate to department head
        Get department head from organizational chart
        Create urgent approval for department head
        Notify original requestor of escalation
        Wait 8 hours
        
    Increment EscalationLevel
    
  If all escalations exhausted:
    Create incident in service management system
    Notify workflow administrators
    Pause workflow pending manual intervention

Exception scenarios require special handling. For example, what happens if a high-value request is submitted just before month-end budget freeze?

Check Business Rules:
  Get current business period:
    HTTP Request: /_api/web/lists/getbytitle('Business Calendar')/items
    Filter: and(
      le(PeriodStart, '@{utcNow()}'),
      ge(PeriodEnd, '@{utcNow()}')
    )
    
  Condition: Budget freeze period
    Expression: equals(body('Get_current_business_period')?['value'][0]['BudgetFreeze'], true)
    
    If yes:
      Condition: Amount exceeds freeze threshold
        Expression: greater(
          triggerOutputs()?['body/Amount'],
          body('Get_current_business_period')?['value'][0]['FreezeThreshold']
        )
        
        If yes:
          Create exception approval:
            Title: "BUDGET FREEZE EXCEPTION - " + @{variables('ApprovalTitle')}
            Assigned to: CFO and CEO
            Details: @{variables('ExceptionApprovalDetails')}
            Urgency: High

For critical system failures during approval processing, we implement graceful degradation:

Error Handling:
  Try:
    Execute normal approval logic
    
  Catch:
    Log error details:
      Error: @{actions('Primary_approval')?['error']}
      RequestId: @{variables('RequestId')}
      Timestamp: @{utcNow()}
      
    Switch on error type:
      Case: "Service Unavailable"
        Queue request for retry
        Send notification: "Approval system temporarily unavailable"
        
      Case: "User Not Found"
        Resolve approver using backup directory
        Create approval with resolved approver
        
      Case: "Permission Denied"
        Create service desk ticket
        Route to manual approval process
        
      Default:
        Create critical incident
        Notify system administrators
        Route to emergency approval process

This comprehensive exception handling ensures that approval workflows continue functioning even when individual components fail or business conditions change unexpectedly.

Integration Patterns with External Systems

Modern approval workflows don't operate in isolation—they integrate with ERP systems, financial platforms, project management tools, and compliance systems. These integrations require sophisticated data mapping, error handling, and synchronization logic.

Let's build an integration with an SAP procurement system that synchronizes approved purchase requests:

SAP Integration:
  Condition: Approval fully completed
    If yes:
      Compose SAP payload:
        Value: {
          "PurchaseRequisition": {
            "RequisitionType": "@{variables('SAPRequisitionType')}",
            "CompanyCode": "@{variables('CompanyCode')}",
            "PurchasingOrganization": "@{variables('PurchasingOrg')}",
            "PurchasingGroup": "@{variables('PurchasingGroup')}",
            "Items": [
              {
                "Material": "@{triggerOutputs()?['body/MaterialCode']}",
                "Quantity": "@{triggerOutputs()?['body/Quantity']}",
                "Unit": "@{triggerOutputs()?['body/UnitOfMeasure']}",
                "DeliveryDate": "@{triggerOutputs()?['body/RequiredDate']}",
                "Plant": "@{variables('Plant')}",
                "StorageLocation": "@{variables('StorageLocation')}",
                "AccountAssignment": {
                  "CostCenter": "@{triggerOutputs()?['body/CostCenter']}",
                  "WBSElement": "@{triggerOutputs()?['body/ProjectCode']}"
                }
              }
            ]
          }
        }
        
      HTTP POST to SAP:
        URI: https://sap-system.company.com/sap/api/purchaserequisition/create
        Headers:
          Authorization: Bearer @{body('Get_SAP_token')?['access_token']}
          Content-Type: application/json
          X-CSRF-Token: @{body('Get_CSRF_token')?['csrf_token']}
        Body: @{outputs('Compose_SAP_payload')}
        
      Parse SAP response:
        Content: @{body('HTTP_POST_to_SAP')}
        Schema: {
          "type": "object",
          "properties": {
            "PurchaseRequisition": {
              "type": "string"
            },
            "Status": {
              "type": "string"
            },
            "Messages": {
              "type": "array"
            }
          }
        }
        
      Update SharePoint with SAP PR number:
        ID: @{triggerOutputs()?['body/ID']}
        SAPPurchaseRequisition: @{body('Parse_SAP_response')?['PurchaseRequisition']}
        SAPStatus: @{body('Parse_SAP_response')?['Status']}

For financial system integration, we need to handle complex data transformations:

Financial System Integration:
  Get chart of accounts mapping:
    HTTP Request: /_api/web/lists/getbytitle('Account Mappings')/items
    Filter: and(
      Department eq '@{triggerOutputs()?['body/Department']}',
      Category eq '@{triggerOutputs()?['body/Category']}',
      Active eq true
    )
    
  Compose financial transaction:
    Value: {
      "TransactionType": "PURCHASE_COMMITMENT",
      "TransactionDate": "@{formatDateTime(utcNow(), 'yyyy-MM-dd')}",
      "Reference": "PR-@{triggerOutputs()?['body/ID']}",
      "Description": "Purchase commitment - @{triggerOutputs()?['body/Description']}",
      "LineItems": [
        {
          "Account": "@{body('Get_chart_of_accounts_mapping')?['value'][0]['ExpenseAccount']}",
          "CostCenter": "@{triggerOutputs()?['body/CostCenter']}",
          "Amount": "@{triggerOutputs()?['body/Amount']}",
          "Currency": "@{triggerOutputs()?['body/Currency']}",
          "TaxCode": "@{variables('TaxCode')}"
        },
        {
          "Account": "@{body('Get_chart_of_accounts_mapping')?['value'][0]['CommitmentAccount']}",
          "Amount": "@{mul(triggerOutputs()?['body/Amount'], -1)}",
          "Currency": "@{triggerOutputs()?['body/Currency']}"
        }
      ]
    }

Project management system integration requires handling project hierarchies and resource allocation:

Project Management Integration:
  Condition: Request linked to project
    Expression: not(empty(triggerOutputs()?['body/ProjectCode']))
    
    If yes:
      Get project details:
        HTTP Request: https://pm-system.company.com/api/projects/@{triggerOutputs()?['body/ProjectCode']}
        Headers:
          Authorization: Bearer @{variables('PMToken')}
          
      Validate project budget:
        Expression: greater(
          body('Get_project_details')?['RemainingBudget'],
          triggerOutputs()?['body/Amount']
        )
        
      If budget sufficient:
        Create budget reservation:
          ProjectCode: @{triggerOutputs()?['body/ProjectCode']}
          Amount: @{triggerOutputs()?['body/Amount']}
          Category: @{triggerOutputs()?['body/Category']}
          ReservationDate: @{utcNow()}
          ExpirationDate: @{addDays(utcNow(), 30)}
          
      If budget insufficient:
        Create budget exception request:
          ProjectManager: @{body('Get_project_details')?['ProjectManager']}
          RequiredAmount: @{triggerOutputs()?['body/Amount']}
          AvailableBudget: @{body('Get_project_details')?['RemainingBudget']}

These integration patterns demonstrate how approval workflows become the orchestration layer for complex business processes spanning multiple systems.

Hands-On Exercise: Building an Advanced Purchase Requisition Workflow

Let's build a complete purchase requisition workflow that demonstrates all the concepts we've covered. This exercise creates a real-world system you can adapt for your organization.

Step 1: Create the SharePoint Foundation

First, create a SharePoint list called "Purchase Requisitions" with these columns:

  • Title (Single line of text)
  • Description (Multiple lines of text)
  • Amount (Currency)
  • Category (Choice: Software, Hardware, Services, Supplies)
  • Department (Single line of text)
  • CostCenter (Single line of text)
  • Vendor (Single line of text)
  • Justification (Multiple lines of text)
  • RequiredDate (Date)
  • Status (Choice: Draft, Pending Approval, Approved, Rejected)
  • ApprovalHistory (Multiple lines of text)
  • SAPPurchaseRequisition (Single line of text)

Create a second list called "Approver Matrix" with these columns:

  • Department (Single line of text)
  • Category (Single line of text)
  • MinAmount (Number)
  • MaxAmount (Number)
  • PrimaryApprover (Person)
  • BackupApprover (Person)
  • RequiresFinanceApproval (Yes/No)
  • RequiresITSecurityReview (Yes/No)

Step 2: Build the Core Workflow

Create a new automated flow triggered when an item is created in the Purchase Requisitions list:

1. Initialize Variables:
   - RequestId (String): "PR-" + formatDateTime(utcNow(), 'yyyy') + "-" + triggerOutputs()?['body/ID']
   - ApprovalStages (Array): []
   - CurrentStage (Integer): 1
   - ApprovalHistory (Array): []

2. Validate Request:
   Condition: Amount greater than 0
   If no: 
     Update item Status to "Invalid - Amount must be greater than 0"
     Terminate
     
   Condition: Required date in future
   If no:
     Update item Status to "Invalid - Required date must be in future"
     Terminate

3. Determine Approval Path:
   Get items from Approver Matrix:
     Filter: and(
       Department eq '@{triggerOutputs()?['body/Department']}',
       Category eq '@{triggerOutputs()?['body/Category']}',
       le(MinAmount, @{triggerOutputs()?['body/Amount']}),
       ge(MaxAmount, @{triggerOutputs()?['body/Amount']})
     )
     
   Compose approval stages based on matrix results

Step 3: Implement Dynamic Approver Assignment

4. Resolve Approvers:
   Apply to each: Approval stages
     Get primary approver availability:
       HTTP: /api/v1.0/users/@{item()?['PrimaryApprover']}/presence
       
     Condition: Approver available
     If yes:
       Set current approver to primary
     If no:
       Set current approver to backup
       Add escalation note to approval history

Step 4: Create Multi-Stage Approval Logic

5. Execute Approval Stages:
   Apply to each: Resolved approval stages
   
     Create approval:
       Title: "Purchase Requisition - Stage @{variables('CurrentStage')}"
       Assigned to: @{item()?['Approver']}
       Details: {
         "requestId": "@{variables('RequestId')}",
         "requestor": "@{triggerOutputs()?['body/Author/DisplayName']}",
         "description": "@{triggerOutputs()?['body/Description']}",
         "amount": @{triggerOutputs()?['body/Amount']},
         "vendor": "@{triggerOutputs()?['body/Vendor']}",
         "justification": "@{triggerOutputs()?['body/Justification']}",
         "stage": "@{variables('CurrentStage')}",
         "totalStages": @{length(variables('ApprovalStages'))}
       }
       
     Wait for approval:
       Approval ID: @{body('Create_approval')?['name']}
       Timeout: 24 hours
       
     Switch on approval outcome:
       Case: Approved
         Append to ApprovalHistory: 
           Stage @{variables('CurrentStage')} approved by @{item()?['Approver']}
         Increment CurrentStage
         Continue to next iteration
         
       Case: Rejected
         Update SharePoint item:
           Status: "Rejected at Stage @{variables('CurrentStage')}"
           ApprovalHistory: @{join(variables('ApprovalHistory'), '; ')}
         Send rejection notification
         Terminate
         
       Case: TimedOut
         Initiate escalation procedure

Step 5: Add Integration and Audit Trail

6. Post-Approval Processing:
   Update SharePoint item:
     Status: "Approved"
     ApprovalHistory: @{join(variables('ApprovalHistory'), '; ')}
     
   Create SAP Purchase Requisition:
     [Implementation from integration section]
     
   Update item with SAP PR number:
     SAPPurchaseRequisition: @{body('Parse_SAP_response')?['PurchaseRequisition']}
     
   Send approval notification:
     To: @{triggerOutputs()?['body/Author/Email']}
     Subject: "Purchase Requisition @{variables('RequestId')} Approved"
     Body: Include approval summary and next steps

7. Create Comprehensive Audit Log:
   For each stage: Log to SharePoint Audit list
   Send telemetry to Application Insights
   Update management dashboard metrics

Step 6: Test and Validate

Test your workflow with various scenarios:

  • Small amounts requiring only department approval
  • Large amounts requiring multiple approvals
  • IT purchases requiring security review
  • Requests during budget freeze periods
  • Timeout and escalation scenarios

Monitor the audit trails and verify that all integrations work correctly.

Common Mistakes & Troubleshooting

Building robust approval workflows involves navigating several common pitfalls that can derail even well-designed systems. Understanding these issues before they occur saves significant debugging time and prevents user frustration.

Mistake 1: Not Handling Parallel Approval Failures Gracefully

Many developers implement parallel approvals without considering what happens when one approver rejects while another is still reviewing. The workflow continues waiting for all responses, leaving the rejected request in limbo.

Wrong approach:
Wait for Finance approval: [Finance approver]
Wait for IT approval: [IT approver]
Condition: Both approved
  Continue workflow

Correct approach:
Apply to each: [Finance approver, IT approver]
  Wait for approval with early exit on rejection
  Switch on outcome:
    Case: Rejected
      Cancel remaining approvals
      Terminate workflow
    Case: Approved
      Increment success counter
If success counter equals total approvers:
  Continue workflow

Mistake 2: Hardcoding Approver Email Addresses

Hardcoded approvers break when people change roles or leave the organization. Always use dynamic lookup mechanisms.

Wrong: Assigned to: "john.manager@company.com"

Correct: 
Get department manager:
  Filter user directory by department and role
Assigned to: @{body('Get_department_manager')?['Email']}

Mistake 3: Not Implementing Proper Timeout Handling

Default timeout behavior often leaves workflows in incomplete states. Implement comprehensive escalation procedures.

Mistake 4: Ignoring Approval Delegation

Power Automate handles delegation automatically, but workflows must account for delegate responses in audit trails and notifications.

Mistake 5: Poor Error Messages in Approval Forms

Generic approval forms confuse approvers and lead to delays. Include all relevant context and clear action items.

Troubleshooting Techniques:

When workflows fail, use these diagnostic approaches:

  1. Enable Detailed Logging: Add logging actions after each major step to track workflow progress
  2. Check Expression Syntax: Use the expression builder to validate complex formulas
  3. Verify Permissions: Ensure the workflow has appropriate permissions for all connected systems
  4. Test with Different User Contexts: Approval behavior changes based on user permissions and organizational relationships
  5. Monitor Integration Points: External system failures often manifest as timeout errors in Power Automate

Performance Considerations:

Large organizations may experience performance issues with complex approval workflows:

  • Batch Operations: Process multiple approvals simultaneously when possible
  • Optimize SharePoint Queries: Use proper filtering and indexing
  • Cache Reference Data: Store frequently accessed data like approver matrices in variables
  • Implement Async Processing: For complex integrations, use child workflows to prevent timeouts

Security Best Practices:

Approval workflows handle sensitive business data and require careful security consideration:

  • Principle of Least Privilege: Grant minimum required permissions to service accounts
  • Secure Integration Endpoints: Use certificate-based authentication for external systems
  • Audit Sensitive Actions: Log all approval decisions and data access
  • Encrypt Sensitive Data: Use Azure Key Vault for credentials and sensitive configuration

Summary & Next Steps

We've built a sophisticated approval workflow system that handles complex routing logic, integrates with multiple external systems, and provides comprehensive audit trails. The purchase requisition workflow demonstrates patterns you can adapt to any approval scenario—from expense reports and project approvals to contract reviews and system access requests.

The key principles we've established form the foundation for enterprise-grade approval systems:

Architecture Principles:

  • Dynamic approver assignment based on business rules rather than static configurations
  • Comprehensive error handling and graceful degradation during system failures
  • Rich audit trails that support compliance requirements and process improvement
  • Integration patterns that treat approval workflows as orchestration layers for complex business processes

Technical Patterns:

  • Multi-stage sequential and parallel approval orchestration
  • Timeout handling with intelligent escalation procedures
  • Real-time validation and business rule enforcement
  • Sophisticated reporting and analytics capabilities

Business Value: Your approval workflows now provide visibility into process bottlenecks, ensure consistent policy enforcement, and adapt automatically to organizational changes. The audit trails enable data-driven process improvements, while the integration patterns connect approvals to downstream business systems.

Next Steps:

  1. Expand Integration Scope: Connect your approval workflows to additional systems like HR platforms, project management tools, and compliance systems

  2. Implement Advanced Analytics: Build predictive models that identify potential approval delays and recommend process optimizations

  3. Create Approval Templates: Develop reusable workflow templates for different approval scenarios, reducing development time for new processes

  4. Explore Mobile Optimization: Enhance the mobile approval experience with custom Power Apps that provide richer interfaces than default approval notifications

  5. Investigate AI Integration: Implement AI Builder models that can pre-classify requests, predict approval likelihood, or extract data from unstructured documents

The patterns and techniques you've learned apply beyond Power Automate—they represent best practices for any workflow automation platform. As you build more complex approval systems, these foundations will enable you to create processes that truly transform how your organization handles approvals and decision-making.

Learning Path: Flow Automation Basics

Previous

Desktop Flows: Automate Legacy Applications with RPA in Power Automate

Next

Power Automate Best Practices: Master Flow Naming, Testing, and Monitoring

Related Articles

Power Automate🌱 Foundation

Power Automate Flow Types Explained: Automated, Instant, and Scheduled Flows

17 min
Power Automate🔥 Expert

Implementing Parallel Branching and Concurrency Control in Power Automate to Maximize Throughput and Prevent Race Conditions

29 min
Power Automate⚡ Practitioner

Scheduling and Managing Time-Based Flows in Power Automate: Recurrence Triggers, Time Zones, and Business Hours Logic*

22 min

On this page

  • Prerequisites
  • Understanding Power Automate's Approval Architecture
  • Designing Complex Approval Routing Logic
  • Building Custom Approval Forms and Data Validation
  • Implementing Multi-Stage Sequential and Parallel Approvals
  • Advanced Approver Assignment and Delegation
  • Building Comprehensive Audit Trails and Reporting
  • Handling Timeouts, Escalations, and Exception Scenarios
  • Integration Patterns with External Systems
  • Summary & Next Steps
  • Hands-On Exercise: Building an Advanced Purchase Requisition Workflow
  • Common Mistakes & Troubleshooting
  • Summary & Next Steps