Wicked Smart Data
LearnInsightsAboutContact
Sign InLet's Build
LearnInsightsAboutContact
Sign InLet's Build
Wicked Smart Data

Intelligence, automation, and expert execution — plus an elite library of free knowledge. We turn complexity into competitive advantage.

Start a conversation

Platform

  • Learning Paths
  • Insights
  • RSS Feed

Company

  • About
  • Contact
  • Work With Us

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Wicked Smart Data. All rights reserved.

Intelligence · Automation · Advantage

All Insights
Power Automate

Automating Windows Notification Area and System Tray Interactions in Power Automate Desktop

Most RPA guides ignore the system tray — but that's exactly where many legacy business apps communicate their status. Learn how to detect tray icon states, capture balloon notifications before they vanish, and trigger flow logic based on system tray events using Power Automate Desktop and PowerShell.

⚡ Practitioner22 min readSep 22, 2026Updated Sep 22, 2026
Automating Windows Notification Area and System Tray Interactions in Power Automate Desktop
On this page
  • Introduction
  • Prerequisites
  • Understanding the Windows Notification Area
  • Capturing Tray Icon UI Elements in PAD
  • Step 1: Expose the Tray Icons
  • Step 2: Record the Target Element
  • Step 3: Make the Selector Robust
  • Step 4: Read the Tooltip Text
  • Building a Polling Loop to Monitor Tray Status
  • Handling the Overflow Tray (Hidden Icons)
  • Intercepting Balloon Notifications
  • Strategy 1: Pre-Position PAD to Watch for the Balloon
  • Strategy 2: Using PowerShell to Read Toast Notification History
  • Triggering Context Menu Actions on Tray Icons
  • Using PowerShell to Query Tray Icon State Without UI Interaction
  • Branching Flow Logic Based on Tray State
  • Hands-On Exercise: Build a Sync Monitor Flow
  • Part 1: Initialize and Validate
  • Part 2: Trigger the Sync
  • Part 3: Poll for Completion
  • Part 4: Branch and Process
  • Common Mistakes & Troubleshooting
  • Problem: "Element not found" errors during tray icon capture
  • Problem: Balloon tips appear and vanish before the flow reacts
  • Problem: The tray selector works on the developer machine but breaks in production (unattended)
  • Problem: PowerShell UserNotificationListener returns empty results
  • Problem: The context menu actions don't click the right item
  • Summary & Next Steps
  • Automating Windows Notification Area and System Tray Interactions in Power Automate Desktop: Detecting App Status Icons, Responding to Balloon Alerts, and Triggering Flow Logic Based on System Tray Events

    Introduction

    Picture this: your organization runs a legacy data synchronization tool that lives entirely in the Windows system tray. It has no API, no webhook, no database you can query. The only signal it gives the outside world is a balloon notification that pops up when a sync completes — or an icon color change when it encounters an error. Your job is to build an automation that catches that signal, reads it, and kicks off downstream processing in Excel and a web portal. No human watching the screen required.

    This is not an edge case. Across industries, critical business applications communicate their status almost exclusively through the Windows notification area. VPN clients, antivirus tools, backup software, ERP agents, and custom middleware all use tray icons and balloon tooltips as their primary status channel. If you're building production RPA flows on Windows machines, you will encounter this problem — and the naive approaches (wait a fixed number of seconds, hope for the best) will fail you in production.

    By the end of this lesson, you'll have the skills to build flows that actively monitor the system tray, read icon tooltips and states programmatically, intercept balloon notifications before they disappear, and branch your flow logic based on what those notifications actually say. We'll combine Power Automate Desktop's native UI automation with PowerShell scripting to cover scenarios where PAD's built-in actions aren't enough on their own.

    What you'll learn:

    • How the Windows notification area works under the hood, and why that matters for automation
    • Using UI element selectors to target system tray icons and read their tooltip text
    • Polling loops and event-driven patterns for detecting tray state changes
    • Capturing balloon notification content before Windows auto-dismisses it
    • Branching flow logic based on tray icon status, tooltip content, and notification text
    • PowerShell techniques for querying tray icon state when UI selectors fall short

    Prerequisites

    This lesson assumes you're comfortable with the PAD designer, can build basic flows with variables and loops, and have at least some exposure to UI element selectors. If you need a refresher on selectors specifically, the lesson on UI Elements and Selectors in Power Automate Desktop: Building Automations That Don't Break covers the fundamentals you'll need here. You should also understand how error handling works in PAD — we'll rely on it heavily when tray interactions go sideways. Review Error Handling in Desktop Flows: On Block Error, Retry Policies, and Recovery Screenshots if you need a refresher.


    Understanding the Windows Notification Area

    Before you write a single action, you need to understand what you're actually targeting. The Windows notification area (the tray) is a region of the taskbar managed by explorer.exe. It hosts two kinds of content: persistent icons (apps that are running and want to show ongoing status) and transient notifications (balloon tips or modern toast notifications that appear for a few seconds and vanish).

    From an automation standpoint, these are fundamentally different problems:

    Persistent tray icons are actual UI elements in a toolbar control. You can reach them with UI automation, read their name (tooltip), check their existence, and right-click them to trigger context menus — as long as you know their selector.

    Balloon notifications are a race condition. They appear for 5–10 seconds by default and disappear whether you interact with them or not. This means your flow needs to be either pre-positioned and waiting, or fast enough to react within that window.

    Modern toast notifications (the ones that stack in Action Center) are a different beast still — they're rendered by the shell's notification platform and can actually be queried even after they've disappeared, which gives us more options.

    Key insight

    The Windows system tray toolbar is a ToolBar control accessible via UI Automation. Its children are Button elements where the button name is the tooltip text of the tray icon. This is why reading tooltip text is actually straightforward once you have the right selector path.

    The tray toolbar lives inside the taskbar window. The internal control hierarchy looks roughly like this:

    Shell_TrayWnd (the taskbar window)
      └─ TrayNotifyWnd
           └─ SysPager
                └─ ToolbarWindow32 ("Notification Area")
                     ├─ Button ("OneDrive - Personal\nUp to date")
                     ├─ Button ("VPN Connected - Corp Network")
                     └─ Button ("DataSync Agent - Last sync: 2:45 PM")
    

    There's a second overflow toolbar inside the "Show hidden icons" flyout, with the same structure. Apps that Windows decides are "lower priority" get pushed there. Your automation needs to handle both locations.


    Capturing Tray Icon UI Elements in PAD

    Let's start with the concrete mechanics of targeting a tray icon. Open the Power Automate Desktop designer and we'll walk through building the selector.

    Step 1: Expose the Tray Icons

    First, make sure the icon you want to target is visible. If it lives in the overflow area, click the chevron (the upward arrow on the taskbar) to open the hidden icons flyout before you start capturing. PAD's UI recorder can only capture elements that are currently visible on screen.

    Step 2: Record the Target Element

    Use the "Add UI element" picker (the crosshair button in the UI elements panel). Hover over the specific tray icon — you should see the highlight border snap around just that button. Press Ctrl+Left Click to capture it.

    The resulting selector will look something like this in PAD's selector editor:

    :desktop > window[Name="Taskbar" OR Name="Shell_TrayWnd"] > 
    custom[AutomationId="TrayNotifyWnd"] > 
    custom[AutomationId="SysPager"] > 
    toolbar[Name="Notification Area"] > 
    button[Name="DataSync Agent - Last sync: 2:45 PM"]
    

    Warning

    That button[Name="..."] attribute is the tooltip text, and it changes every time the sync completes (because the timestamp updates). If you leave the selector as-is, it will fail the next time you run it. You have two options: use a partial match on a stable prefix, or target the icon by its ordinal position if the icon order is predictable.

    Step 3: Make the Selector Robust

    In PAD's selector editor, switch the Name attribute on the button from an exact match to a "Contains" match. If your selector editor supports regular expressions, use DataSync Agent as the match string. The selector becomes:

    :desktop > window[Name="Taskbar" OR Name="Shell_TrayWnd"] > 
    custom[AutomationId="TrayNotifyWnd"] > 
    custom[AutomationId="SysPager"] > 
    toolbar[Name="Notification Area"] > 
    button[Name contains "DataSync Agent"]
    

    Now it will match regardless of what timestamp is appended.

    Step 4: Read the Tooltip Text

    Once you have the element captured, use the Get details of UI element in window action with the property set to Name. This returns the full tooltip string, including any dynamic content like status messages or timestamps. Store this in a variable — TrayIconTooltip — for use in your flow logic.

    Here's how that action block looks in structured pseudocode:

    Get details of UI element in window
      UI element: TrayIcon_DataSyncAgent
      Attribute: Name
      Store result in: TrayIconTooltip
    

    Building a Polling Loop to Monitor Tray Status

    For most real workflows, you don't want to read the tray icon once and move on — you want to watch it until something changes. This means building a polling loop.

    The classic pattern is a Loop with a Wait action inside, checking a condition on each iteration. Here's a complete polling flow that watches the DataSync Agent icon and exits the loop when it detects either a "Sync complete" or "Error" status:

    SET MaxWaitSeconds TO 300
    SET PollIntervalSeconds TO 5
    SET ElapsedSeconds TO 0
    SET SyncStatus TO "Unknown"
    
    LOOP
        # Try to read the tray icon tooltip
        ON BLOCK ERROR
            SET TrayIconFound TO False
        END
        
        Get details of UI element in window
            UI element: TrayIcon_DataSyncAgent
            Attribute: Name
            Store result in: TrayIconTooltip
        
        SET TrayIconFound TO True
        
        IF TrayIconTooltip CONTAINS "Sync complete" THEN
            SET SyncStatus TO "Complete"
            EXIT LOOP
        END IF
        
        IF TrayIconTooltip CONTAINS "Error" OR TrayIconTooltip CONTAINS "Failed" THEN
            SET SyncStatus TO "Error"
            EXIT LOOP
        END IF
        
        Wait PollIntervalSeconds seconds
        SET ElapsedSeconds TO ElapsedSeconds + PollIntervalSeconds
        
        IF ElapsedSeconds >= MaxWaitSeconds THEN
            SET SyncStatus TO "Timeout"
            EXIT LOOP
        END IF
    END LOOP
    

    Tip

    Set your poll interval based on how quickly the application updates its tray icon. For most business tools, 5 seconds is a reasonable default that's responsive without hammering the UI automation framework. For high-frequency monitoring (sub-second), you'll want PowerShell instead.

    After the loop, SyncStatus will be one of "Complete", "Error", or "Timeout", and you can branch your downstream logic accordingly. This is much cleaner than building deeply nested If blocks.


    Handling the Overflow Tray (Hidden Icons)

    Here's where a lot of automations break: if the icon you're monitoring lives in the hidden icons overflow area, it won't be in the main toolbar — it'll be in a separate flyout window that only exists when open. You need to open it first.

    Add a block before your polling loop:

    # Check if icon is in main tray first
    ON BLOCK ERROR
        # Icon not in main tray, try overflow
        
        Click UI element: ShowHiddenIcons_Button
        # This clicks the chevron/arrow to open the overflow
        
        Wait 1 second
        # Now capture from the overflow toolbar
    END
    

    The overflow flyout window has a different selector root:

    :desktop > window[Name="Overflow Notification Area"] >
    toolbar[Name="Overflow Notification Area"] >
    button[Name contains "DataSync Agent"]
    

    Warning

    The overflow flyout closes automatically when it loses focus, and PAD's UI automation actions can sometimes cause focus loss. If your icon is in the overflow, consider using PowerShell to query the tray state instead — it doesn't require the flyout to be open. We'll cover that approach in the PowerShell section below.


    Intercepting Balloon Notifications

    Balloon notifications are the harder problem. They appear briefly and disappear. The naive approach — "take a screenshot and OCR it" — is flaky because the timing is unpredictable. Let's look at more reliable strategies.

    Strategy 1: Pre-Position PAD to Watch for the Balloon

    If you know roughly when a balloon will appear, you can set up a loop that's actively looking for the balloon window. Balloon tips in classic Win32 applications create a tooltips_class32 window. Modern applications use toast notifications via Windows.UI.Notifications.

    For classic balloon tips, set up a window-existence check loop:

    SET BalloonCaptured TO False
    SET BalloonCheckCount TO 0
    SET MaxBalloonChecks TO 60  # 60 * 0.5 seconds = 30 second window
    
    LOOP WHILE BalloonCheckCount < MaxBalloonChecks AND BalloonCaptured = False
        
        IF window exists [Class="tooltips_class32"] THEN
            Get details of UI element in window
                Window: tooltips_class32
                Element: static text
                Attribute: Name
                Store in: BalloonText
            
            SET BalloonCaptured TO True
        END IF
        
        Wait 0.5 seconds
        SET BalloonCheckCount TO BalloonCheckCount + 1
    
    END LOOP
    

    Note

    The tooltips_class32 window class is used by older Win32 applications for balloon tips. Applications built with newer frameworks (WPF, WinUI, modern Electron apps) use completely different notification mechanisms. If your target app uses toast notifications, skip ahead to the toast approach.

    Strategy 2: Using PowerShell to Read Toast Notification History

    Modern Windows toast notifications have a significant advantage over balloon tips: they're logged in the notification platform's history, and you can query that history even after the notification has been dismissed. This completely eliminates the race condition.

    Use PAD's Run PowerShell script action with this script:

    # Query Windows notification history for notifications from a specific app
    Add-Type -AssemblyName System.Runtime.WindowsRuntime
    
    $asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | 
        Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and 
                       $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
    
    Function Await($WinRtTask, $ResultType) {
        $asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
        $netTask = $asTask.Invoke($null, @($WinRtTask))
        $netTask.Wait(-1) | Out-Null
        $netTask.Result
    }
    
    [Windows.UI.Notifications.Management.UserNotificationListener, Windows.UI.Notifications.Management, ContentType = WindowsRuntime] | Out-Null
    [Windows.UI.Notifications.UserNotificationChangedKind, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
    
    $listener = [Windows.UI.Notifications.Management.UserNotificationListener]::Current
    
    $notificationsAsync = $listener.GetNotificationsAsync(
        [Windows.UI.Notifications.NotificationKinds]::Toast
    )
    
    $notifications = Await $notificationsAsync ([System.Collections.Generic.IReadOnlyList[Windows.UI.Notifications.UserNotification]])
    
    # Filter by app name and get the most recent
    $targetNotifications = $notifications | Where-Object {
        $_.AppInfo.DisplayInfo.DisplayName -like "*DataSync*"
    } | Sort-Object { $_.CreationTime } -Descending
    
    if ($targetNotifications.Count -gt 0) {
        $latest = $targetNotifications[0]
        $toast = $latest.Notification.Visual.GetBinding(
            [Windows.UI.Notifications.KnownNotificationBindings]::ToastGeneric
        )
        
        if ($toast -ne $null) {
            $textElements = $toast.GetTextElements()
            $output = ($textElements | ForEach-Object { $_.Text }) -join "|"
            Write-Output $output
        }
    } else {
        Write-Output "NO_NOTIFICATION_FOUND"
    }
    

    Capture the output in PAD using the PowershellOutput variable that the Run PowerShell script action returns. Parse it by splitting on | to get individual text lines from the notification.

    This approach is much more reliable for production flows. The notification history is retained even after dismissal, so you don't have to win a timing race.

    Key insight

    The UserNotificationListener API requires that your app (in this case, the PowerShell process) has been granted access permission by the user. The first time this runs, Windows may prompt for permission. In unattended scenarios, pre-grant this access manually or through group policy before deploying your flow.

    For more on integrating PowerShell into your PAD flows, see Scripting Inside Desktop Flows: Running PowerShell, Python, and VBScript Actions.


    Triggering Context Menu Actions on Tray Icons

    Sometimes you don't just need to read a tray icon's status — you need to interact with it. Right-clicking a tray icon opens a context menu. Here's how to do that reliably.

    Right-click UI element: TrayIcon_DataSyncAgent
    
    # Wait for context menu to appear
    Wait for UI element to exist: ContextMenu_DataSync
    Timeout: 5 seconds
    
    # Click specific menu item
    Click UI element: MenuItem_ForceSyncNow
    

    The tricky part is that context menus are transient windows that don't appear in your UI element tree until they're visible. Capture the context menu items while the menu is open (right-click manually to open it, then use "Add UI element" to capture the items), and the selector will work when your flow triggers it programmatically.

    Tip

    Right-click context menus on tray icons sometimes require a brief delay between the right-click and the menu appearing, especially on slower machines or RDP sessions. Add a 0.5-second Wait action between the right-click and the subsequent menu interaction to avoid "element not found" errors.

    For a deeper dive on handling transient UI elements like menus and dialogs, see Automating Windows Dialog Boxes and Pop-Up Windows in Power Automate Desktop: Handling Alerts, File Pickers, and Modal Prompts Reliably.


    Using PowerShell to Query Tray Icon State Without UI Interaction

    When UI automation approaches are unreliable — especially in unattended runs where the desktop session might be locked — PowerShell gives you a more direct path to tray icon information. The Windows Shell exposes tray icon data through COM interfaces.

    Here's a PowerShell script that enumerates all visible tray icons and their tooltips without requiring any UI interaction:

    # Enumerate system tray icons using Shell COM interface
    $Shell = New-Object -ComObject Shell.Application
    
    # Access the system tray
    Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    using System.Text;
    
    public class TrayInfo {
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr FindWindowEx(
            IntPtr hwndParent, IntPtr hwndChildAfter, 
            string lpszClass, string lpszWindow);
        
        [DllImport("user32.dll")]
        public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        
        [DllImport("user32.dll")]
        public static extern bool EnumChildWindows(
            IntPtr hwndParent, EnumChildCallback lpEnumFunc, IntPtr lParam);
        
        public delegate bool EnumChildCallback(IntPtr hwnd, IntPtr lParam);
    }
    "@
    
    # Find the notification area toolbar
    $taskbar = [TrayInfo]::FindWindow("Shell_TrayWnd", $null)
    $trayNotify = [TrayInfo]::FindWindowEx($taskbar, [IntPtr]::Zero, "TrayNotifyWnd", $null)
    $sysPager = [TrayInfo]::FindWindowEx($trayNotify, [IntPtr]::Zero, "SysPager", $null)
    $toolbar = [TrayInfo]::FindWindowEx($sysPager, [IntPtr]::Zero, "ToolbarWindow32", $null)
    
    $sb = New-Object System.Text.StringBuilder(256)
    [TrayInfo]::GetWindowText($toolbar, $sb, 256)
    
    Write-Output "Toolbar found: $($sb.ToString())"
    
    # Use UI Automation .NET directly for cleaner access
    Add-Type -AssemblyName UIAutomationClient
    Add-Type -AssemblyName UIAutomationTypes
    
    $automation = [System.Windows.Automation.AutomationElement]
    $root = $automation::RootElement
    $condition = New-Object System.Windows.Automation.PropertyCondition(
        $automation::NameProperty, "Notification Area"
    )
    
    $notifArea = $root.FindFirst(
        [System.Windows.Automation.TreeScope]::Descendants, 
        $condition
    )
    
    if ($notifArea -ne $null) {
        $buttonCondition = New-Object System.Windows.Automation.PropertyCondition(
            $automation::ControlTypeProperty, 
            [System.Windows.Automation.ControlType]::Button
        )
        
        $buttons = $notifArea.FindAll(
            [System.Windows.Automation.TreeScope]::Children, 
            $buttonCondition
        )
        
        $results = @()
        foreach ($button in $buttons) {
            $name = $button.GetCurrentPropertyValue($automation::NameProperty)
            $results += $name
        }
        
        $results -join "||"
    } else {
        Write-Output "NOTIFICATION_AREA_NOT_FOUND"
    }
    

    Capture the output and split on || in PAD to get a list of all current tray icon tooltips. You can then search that list for your target application:

    Run PowerShell script: [script above]
    Store output in: TrayIconsRaw
    
    Split text: TrayIconsRaw
    Delimiter: "||"
    Store result in: TrayIconsList
    
    FOR EACH icon IN TrayIconsList
        IF icon CONTAINS "DataSync Agent" THEN
            SET DataSyncIconText TO icon
            EXIT LOOP
        END IF
    END FOR EACH
    

    This is particularly valuable in unattended automation contexts. If you're running your flows without an active desktop session, see Attended vs Unattended RPA: Choosing a Run Mode and Configuring Machines in Power Automate for the full picture on session requirements.


    Branching Flow Logic Based on Tray State

    Now let's put this together into a complete decision-making pattern. After you've captured the tray icon tooltip or notification text, you need to route your flow to the right path.

    Here's a realistic scenario: the DataSync Agent tray icon can show five states. Each requires different downstream handling:

    # Assume DataSyncIconText is populated from polling loop above
    
    IF DataSyncIconText CONTAINS "Sync complete" THEN
        # Happy path: process the synced data
        RUN SUBFLOW: ProcessSyncedData
        
    ELSE IF DataSyncIconText CONTAINS "Sync in progress" THEN
        # Still running - not ready yet, extend the wait
        SET SyncStatus TO "InProgress"
        Wait 30 seconds
        # Re-enter polling (or use a flag to continue the outer loop)
        
    ELSE IF DataSyncIconText CONTAINS "Error" OR DataSyncIconText CONTAINS "Failed" THEN
        # Extract the error message from the tooltip
        # Tooltip format: "DataSync Agent - Error: Connection timeout (Code 1042)"
        
        Split text: DataSyncIconText
        Separator: "Error: "
        Store in: ErrorParts
        
        SET ErrorDetail TO ErrorParts[1]  # "Connection timeout (Code 1042)"
        
        RUN SUBFLOW: HandleSyncError
            Input: ErrorDetail
        
    ELSE IF DataSyncIconText CONTAINS "Paused" THEN
        # Someone manually paused the agent - alert and stop
        Send email notification
        SET FlowResult TO "ManualIntervention"
        EXIT FLOW
        
    ELSE
        # Unknown state - log and continue monitoring
        Append to log file: "Unknown tray state: " + DataSyncIconText
        
    END IF
    

    Tip

    Build your status parsing logic as a dedicated subflow that accepts the raw tooltip text and returns a standardized status code. This makes your main flow cleaner and lets you reuse the same parsing logic across multiple flows that interact with the same application. For more on this pattern, see Subflows and Reusable Logic in Power Automate Desktop.


    Hands-On Exercise: Build a Sync Monitor Flow

    Let's build a complete, working example. This exercise creates a flow that monitors a hypothetical data synchronization tool's tray icon, waits for sync completion, logs the outcome, and triggers downstream Excel processing.

    Setup: For this exercise, you can simulate the target application by using any application that shows a tray icon — Slack, OneDrive, or even Windows Defender will work for practicing the UI element capture. Replace the selector references with your actual target application.

    The flow has four parts:

    Part 1: Initialize and Validate

    # Set up tracking variables
    SET FlowStartTime TO Current date and time
    SET SyncStatus TO "Pending"
    SET MaxWaitMinutes TO 10
    SET PollIntervalSeconds TO 5
    
    # Verify the sync agent is running before starting
    Run PowerShell:
        $proc = Get-Process "DataSyncAgent" -ErrorAction SilentlyContinue
        if ($proc) { Write-Output "RUNNING" } else { Write-Output "NOT_RUNNING" }
    Store in: AgentStatus
    
    IF AgentStatus DOES NOT EQUAL "RUNNING" THEN
        Log event: "DataSync Agent not running at flow start"
        THROW ERROR "Agent process not found"
    END IF
    

    Part 2: Trigger the Sync

    # Right-click tray icon and select "Start Sync Now"
    Right-click UI element: TrayIcon_DataSyncAgent
    Wait 1 second
    Click UI element: MenuItem_StartSyncNow
    
    # Record trigger time
    SET SyncTriggerTime TO Current date and time
    Log event: "Sync triggered at " + SyncTriggerTime
    

    Part 3: Poll for Completion

    SET ElapsedSeconds TO 0
    SET MaxSeconds TO MaxWaitMinutes * 60
    
    LOOP
        ON BLOCK ERROR
            Log event: "Failed to read tray icon at elapsed " + ElapsedSeconds + "s"
            SET TrayReadFailed TO True
        ELSE
            SET TrayReadFailed TO False
        END
        
        IF TrayReadFailed = False THEN
            Get details of UI element: TrayIcon_DataSyncAgent
            Attribute: Name
            Store in: CurrentTooltip
            
            IF CurrentTooltip CONTAINS "Sync complete" THEN
                SET SyncStatus TO "Complete"
                EXIT LOOP
            END IF
            
            IF CurrentTooltip CONTAINS "Error" OR CurrentTooltip CONTAINS "Failed" THEN
                SET SyncStatus TO "Error"
                SET SyncErrorDetail TO CurrentTooltip
                EXIT LOOP
            END IF
        END IF
        
        Wait PollIntervalSeconds seconds
        SET ElapsedSeconds TO ElapsedSeconds + PollIntervalSeconds
        
        IF ElapsedSeconds >= MaxSeconds THEN
            SET SyncStatus TO "Timeout"
            EXIT LOOP
        END IF
    END LOOP
    

    Part 4: Branch and Process

    IF SyncStatus = "Complete" THEN
        # Open Excel and run the post-sync processing macro
        Open Excel workbook: "C:\Reports\SyncProcessing.xlsx"
        Run Excel macro: "ProcessNewSyncData"
        Save and close workbook
        
        Log event: "Sync completed and processed successfully"
        
    ELSE IF SyncStatus = "Error" THEN
        # Write error to tracking sheet
        Open Excel workbook: "C:\Reports\SyncErrors.xlsx"
        Write to cell: [next empty row, Col A] = FlowStartTime
        Write to cell: [next empty row, Col B] = SyncErrorDetail
        Save and close workbook
        
        # Send alert email
        Send email:
            To: "ops-team@company.com"
            Subject: "DataSync Agent Error - Action Required"
            Body: "Sync failed with: " + SyncErrorDetail
        
    ELSE IF SyncStatus = "Timeout" THEN
        Send email:
            To: "ops-team@company.com"
            Subject: "DataSync Agent Timeout - Sync Did Not Complete"
            Body: "Flow waited " + MaxWaitMinutes + " minutes without sync completion."
            
    END IF
    

    For the Excel interactions in Part 4, see Automating Excel with Power Automate Desktop: Reading, Writing, and Running Macros for the action specifics.


    Common Mistakes & Troubleshooting

    Problem: "Element not found" errors during tray icon capture

    Root cause: The tray icon isn't visible (it's in the overflow), the application isn't running, or the element has moved because the icon order changed.

    Fix: Add an explicit check for the overflow area before attempting to read the main tray. Use error handling to distinguish "application not running" (serious problem) from "icon in overflow" (solvable problem). Build your selector with Contains matching on the app name rather than exact text matching.

    Problem: Balloon tips appear and vanish before the flow reacts

    Root cause: Classic balloon tips have a very short display window, and if your flow is mid-action on another element, it will miss them.

    Fix: Switch to the PowerShell-based notification history approach for any application that uses modern Windows notifications. For genuinely old Win32 balloon tips, the only reliable approach is a tight polling loop (0.25–0.5 second intervals) that's running before the notification is expected to appear. For production flows, consider using Extracting Text from PDFs, Images, and Scanned Documents with OCR in Power Automate Desktop as a fallback — OCR a screenshot the moment you detect the tooltips_class32 window.

    Problem: The tray selector works on the developer machine but breaks in production (unattended)

    Root cause: Unattended runs execute in a different Windows session, often with a locked or minimized desktop. The notification area toolbar may not be rendered or accessible in that session state.

    Fix: First, verify that your unattended machine is configured to run with an interactive session where the tray is visible. See Attended vs Unattended RPA: Choosing a Run Mode and Configuring Machines in Power Automate for session configuration details. Second, use the PowerShell UI Automation approach from earlier — it's more robust in non-interactive sessions than PAD's native UI element actions.

    Problem: PowerShell UserNotificationListener returns empty results

    Root cause: The notification listener permission hasn't been granted, or the notification platform service isn't running.

    Fix: Run this check in your PowerShell script before querying notifications:

    $listener = [Windows.UI.Notifications.Management.UserNotificationListener]::Current
    $accessStatus = $listener.RequestAccessAsync().AsTask().Result
    
    if ($accessStatus -ne [Windows.UI.Notifications.Management.UserNotificationListenerAccessStatus]::Allowed) {
        Write-Output "ACCESS_DENIED: $accessStatus"
        exit 1
    }
    

    If it returns ACCESS_DENIED, the permission needs to be granted manually in Windows Settings (Notifications & Actions) or via MDM policy before the flow can run.

    Problem: The context menu actions don't click the right item

    Root cause: Context menu item positions can change based on application state (grayed out items shift the layout), and if you captured the selector for "Start Sync Now" when a sync was idle, the same menu might show "Stop Sync" in the same position when a sync is running.

    Fix: Always select context menu items by their Name attribute (the menu item text), never by position. Check the item's enabled state before clicking:

    Get details of UI element: MenuItem_StartSyncNow
    Attribute: IsEnabled
    Store in: MenuItemEnabled
    
    IF MenuItemEnabled = True THEN
        Click UI element: MenuItem_StartSyncNow
    ELSE
        Log event: "StartSyncNow menu item is disabled - sync may already be in progress"
    END IF
    

    Warning

    Always close a context menu you've opened if you decide not to click anything. An open context menu will block other UI interactions. Use the Escape key action (Send keys: {Escape}) to dismiss it cleanly.


    Summary & Next Steps

    You now have a complete toolkit for working with the Windows notification area in Power Automate Desktop. The core skills to take away:

    • Tray icons are UI elements in a toolbar control, reachable with the same selector machinery you use everywhere else in PAD — just with Contains matching to handle dynamic tooltip text.
    • Polling loops with error handling are the right pattern for monitoring tray state over time. Build them with a timeout and a status variable so downstream logic can branch cleanly.
    • Balloon tips require a timing strategy — either pre-positioned polling at tight intervals, or the PowerShell UserNotificationListener approach for modern toast notifications, which avoids the race condition entirely.
    • PowerShell is your escape hatch when PAD's native UI automation falls short, especially in unattended sessions where the interactive desktop isn't fully available.
    • Branch logic on status codes, not raw tooltip strings. Parse the tooltip into a clean status variable first, then route your flow. It makes flows dramatically easier to maintain.

    From here, consider extending this pattern to multi-application scenarios — tray monitoring is often just one trigger in a larger orchestration. If your flow needs to react to tray events and then transfer data between applications, Automating Multi-Application Workflows in Power Automate Desktop: Transferring Data Between Windows Apps, Web Browsers, and Excel in a Single Desktop Flow covers the broader orchestration patterns.

    For production deployments of flows like this — where the tray monitoring needs to run reliably 24/7 without manual intervention — invest time in understanding your session management and machine configuration. The difference between a flow that works in your dev environment and one that holds up in production is almost always the session setup.

    Work With Us

    From insight to implementation

    Reading is the start. When you're ready to build the data, automation, or AI systems behind it, our team turns strategy into shipped results.

    Let's Build

    Power Automate Desktop & RPA

    Previous

    Automating PDF Form Filling and Digital Signature Workflows in Power Automate Desktop

    Next

    Automating Microsoft Access Database Operations with Power Automate Desktop: Opening Reports, Running Queries, and Exporting Results to Excel in Unattended RPA Workflows

    Related Insights

    Power AutomateExpert

    Automating Microsoft Access Database Operations with Power Automate Desktop: Opening Reports, Running Queries, and Exporting Results to Excel in Unattended RPA Workflows

    29 min
    Power AutomateExpert

    Automating PDF Form Filling and Digital Signature Workflows in Power Automate Desktop

    26 min
    Power AutomateExpert

    Automating Outlook Desktop Client Operations with Power Automate Desktop: Reading Emails, Extracting Attachments, and Triggering Actions Based on Message Content Without Cloud Connectors

    27 min

    On this page

    • Introduction
    • Prerequisites
    • Understanding the Windows Notification Area
    • Capturing Tray Icon UI Elements in PAD
    • Step 1: Expose the Tray Icons
    • Step 2: Record the Target Element
    • Step 3: Make the Selector Robust
    • Step 4: Read the Tooltip Text
    • Building a Polling Loop to Monitor Tray Status
    • Handling the Overflow Tray (Hidden Icons)
    • Intercepting Balloon Notifications
    • Strategy 1: Pre-Position PAD to Watch for the Balloon
    • Strategy 2: Using PowerShell to Read Toast Notification History
    • Triggering Context Menu Actions on Tray Icons
    • Using PowerShell to Query Tray Icon State Without UI Interaction
    • Branching Flow Logic Based on Tray State
    • Hands-On Exercise: Build a Sync Monitor Flow
    • Part 1: Initialize and Validate
    • Part 2: Trigger the Sync
    • Part 3: Poll for Completion
    • Part 4: Branch and Process
    • Common Mistakes & Troubleshooting
    • Problem: "Element not found" errors during tray icon capture
    • Problem: Balloon tips appear and vanish before the flow reacts
    • Problem: The tray selector works on the developer machine but breaks in production (unattended)
    • Problem: PowerShell UserNotificationListener returns empty results
    • Problem: The context menu actions don't click the right item
    • Summary & Next Steps