Learn how to build production-grade RPA integrations by passing dynamic inputs from cloud flows into Power Automate Desktop and returning structured outputs back. Covers attended and unattended modes, DataTable outputs, error patterns, and real-world architecture.

Picture this: your company's ERP system is a 20-year-old Windows application that doesn't have an API, doesn't connect to SharePoint, and definitely doesn't know what a webhook is. But every morning, your finance team needs to pull yesterday's revenue figures from that system, cross-reference them with purchase orders in a separate legacy app, and post the results to a SharePoint list for executive reporting. You've built beautiful cloud flows before — event-driven, conditional, elegant. But they can't reach inside a desktop application. That's where the bridge between cloud and desktop becomes essential.
The ability to trigger a desktop flow from a cloud flow, pass it meaningful input data, and get structured output back is the architectural foundation of enterprise RPA. It's what separates a one-off desktop automation from a production-grade integration. When you master this handoff, you can schedule an unattended robot to wake up at 2 AM, receive a customer ID from a SharePoint item, scrape the ERP for that customer's balance, and return the result directly to your cloud flow — which then sends an alert, updates a record, or kicks off an approval chain.
By the end of this lesson, you'll be able to do exactly that. We'll go deep on how the cloud-to-desktop connection works under the hood, how to define typed inputs and outputs in your desktop flows, how to map them correctly in the cloud flow designer, and how to handle the real-world problems that come up when this integration breaks.
What you'll learn:
You should already know how to build and run a basic desktop flow in Power Automate Desktop (PAD). If you're newer to PAD, review Getting Started with Power Automate Desktop: Installing, Recording, and Running Your First Desktop Flow before continuing. You should also be comfortable working in the cloud flow designer — understanding triggers, actions, and dynamic content. The concepts in Working with Conditions, Loops, and Variables in Power Automate will help you use desktop flow outputs effectively once you get them back into the cloud.
You'll need:
Before we touch any UI, let's understand the plumbing. Cloud flows run in Microsoft's cloud infrastructure — Azure, effectively. Desktop flows run on a physical or virtual Windows machine. These are two completely different execution environments. For a cloud flow to trigger a desktop flow, there must be an agent running on the Windows machine that maintains an outbound connection to the Power Automate service.
That agent is the Power Automate Desktop machine runtime. When PAD is installed and the machine is registered in Power Automate, it establishes an ongoing connection to the cloud. When your cloud flow fires and hits the "Run a flow built with Power Automate Desktop" action, it sends a message through that channel. The runtime on the machine picks it up, launches the desktop flow (either in attended or unattended mode), and when the flow completes, it sends the output variables back through the same channel to the waiting cloud flow.
This architecture has an important implication: the machine must be on and reachable when the cloud flow triggers. For attended automation, a user must be logged in. For unattended automation (the most powerful scenario), the machine can be locked or running in a remote session, but it must be powered on and the runtime agent must be running.
Note
The Power Automate machine runtime replaced the older on-premises data gateway approach for desktop flow connectivity. If you're working in a legacy environment that still uses the gateway, the cloud flow action looks slightly different — you'll see a gateway connection instead of a machine connection. The input/output mechanics are identical either way, but gateway-based setups are being phased out. Prefer the machine runtime agent wherever possible.
Machine groups are particularly useful in production: you register multiple machines under a single group, and Power Automate load-balances runs across available machines. This is critical for scaling unattended RPA without manually managing which machine is busy.
Let's start where the data originates — inside the desktop flow itself. Power Automate Desktop exposes a special category called Input/Output variables. These are distinct from internal flow variables: they're declared at the flow boundary and become the contract between your desktop flow and its caller.
Open your desktop flow in the PAD designer. On the left panel, look for the Variables pane (the {x} icon). At the top of that pane, you'll see a section labeled "Input/output variables" with a + icon. Click it.
A dialog appears asking you to define the variable. Here's what each field means:
CustomerIDCustomer ID. This is what the cloud flow designer will display when you're mapping inputs.For our ERP scenario, let's define three input variables:
| Variable Name | External Name | Data Type | Default Value |
|---|---|---|---|
CustomerID |
Customer ID | Text | TEST001 |
ReportDate |
Report Date | Text | 2024-01-15 |
IncludePending |
Include Pending | Boolean | False |
Tip
Always define a sensible default value for every input variable. When you're building and testing the desktop flow in isolation, PAD uses these defaults so the flow can run without a cloud caller. Without defaults, standalone runs will immediately error on any action that references an unset input variable.
Once you've saved the input variables, you'll see them appear in the Variables pane under the "Input/output variables" section with an arrow-in icon (for inputs) and arrow-out icon (for outputs). Inside your flow actions, you reference them exactly like any other variable: %CustomerID%, %ReportDate%, %IncludePending%.
Here's how you might use them early in the flow to navigate to the right record in the ERP:
// Using the input variables to drive ERP navigation
Set variable: SearchTerm = %CustomerID%
// Launch ERP application
Launch application: "C:\ERPSystem\ERPClient.exe"
// Wait for main window
Wait for window: "ERP Main Dashboard"
// Click the search field and enter the customer ID
Click UI element: SearchField
Populate text field: %CustomerID%
// Click date filter dropdown and set to ReportDate
Click UI element: DateFilterField
Populate text field: %ReportDate%
// Conditionally check the "Include Pending" checkbox
IF %IncludePending% = True THEN
Check checkbox: PendingTransactionsCheckbox
END IF
This is the power of input variables: your desktop flow becomes parameterized. The same flow logic can run for any customer, any date, any combination of options — driven entirely by what the cloud flow passes in.
Outputs work the same way structurally, but they carry results back to the cloud. You define them the same way in the Input/output variables dialog, just select Output as the direction.
For our ERP scenario, let's say we want to return three values:
| Variable Name | External Name | Data Type |
|---|---|---|
TotalRevenue |
Total Revenue | Number |
TransactionCount |
Transaction Count | Number |
StatusMessage |
Status Message | Text |
Inside your desktop flow, you assign values to output variables using the Set variable action, just like any other variable. The key discipline here: always assign your output variables before the flow ends, even in error paths.
// After scraping the ERP results screen...
// Extract revenue figure from UI element
Get text from UI element: RevenueField -> ExtractedText
// Convert to number (it comes as text with currency symbol)
Replace text: ExtractedText replace "$" with "" -> CleanRevenue
Replace text: CleanRevenue replace "," with "" -> CleanRevenue
Convert text to number: CleanRevenue -> TotalRevenue
// Extract transaction count
Get text from UI element: TransactionCountField -> ExtractedText
Convert text to number: ExtractedText -> TransactionCount
// Set status
Set variable: StatusMessage = "Success - Data retrieved for " + %CustomerID%
Warning
If your desktop flow throws an unhandled error and exits before reaching the lines that set output variables, the cloud flow will receive empty/null values for those outputs — and it may not know whether the flow succeeded or failed. This is a critical reason to implement error handling in your desktop flows with On Block Error handlers that set meaningful StatusMessage values before rethrowing or stopping.
One practical pattern: define a FlowStatus output variable (Text type) and set it to "Success" at the very end of your happy path, and "Failed: [error description]" inside error handlers. Your cloud flow can then check this variable to decide what to do next, rather than relying solely on whether the desktop flow threw an exception.
Now let's move to the cloud side. Create or open a cloud flow with whatever trigger makes sense for your scenario. For our ERP reporting case, let's use a scheduled trigger that fires weekdays at 6 AM — and then loops through SharePoint list items to process each customer.
If you want to learn more about how Power Automate triggers work before setting this up, that article covers the full spectrum from webhooks to recurrence.
In the flow designer, add a new step and search for "Run a flow built with Power Automate Desktop". This is the connector action that bridges the two worlds.
The action has several configuration fields:
This is the most consequential choice:
For production scheduled automation, you almost always want Unattended.
Select your desktop flow from the dropdown. This lists all desktop flows in your current environment that your account has access to. If you just created the flow in PAD, it should appear here after it's been saved and published.
This is where you specify which machine (or group) runs the flow. Select the registered machine from the dropdown. For production, select a machine group so runs aren't blocked if one machine is busy.
For unattended runs, Power Automate needs Windows credentials to log into the machine (or use the existing session). You'll configure a connection that stores the username and password of the Windows account to use. This is separate from your Power Automate credentials.
Key insight
Your unattended machine connection stores Windows login credentials for the target machine. These are not your Microsoft 365 credentials — they're the local or domain Windows account credentials for the automation machine. Store them carefully, and consider using Azure Key Vault integration rather than hardcoding credentials in connections you share with colleagues.
Once you've selected your desktop flow, the action dynamically renders fields for every input variable you defined in PAD. You'll see fields labeled with the External name you set earlier.
Here's where it gets exciting. Each input field accepts dynamic content from your cloud flow. For our scenario, the cloud flow might be iterating over a SharePoint list of customers. You'd map:
Title column from the SharePoint item (dynamic content)formatDateTime(utcNow(), 'yyyy-MM-dd') (expression)IncludePending column from the SharePoint item (dynamic content, Boolean)The type coercion matters here. If your PAD variable is Number type, and you pass a cloud flow string that looks like a number, PAD will attempt to coerce it — but it can silently fail. Pass the right type from the cloud flow. For Boolean inputs, use true/false literals or actual Boolean-typed dynamic content, not the strings "true" or "false".
After the "Run a flow built with Power Automate Desktop" action executes, the outputs your desktop flow returned become available as dynamic content in all subsequent cloud flow actions.
In the cloud flow designer, click into any subsequent action's field and open the dynamic content panel. You'll see a section labeled with your desktop flow's name, containing entries for each output variable — labeled by their External name.
For our ERP scenario, you'd see:
You can use these exactly like any other dynamic content. Let's say we want to write the results back to SharePoint and send a summary email:
Update SharePoint Item action:
Total Revenue (from dynamic content)Transaction CountStatus MessageutcNow()Send email action (if revenue is above threshold):
Subject: ERP Report Ready - @{items('Apply_to_each')?['Title']}
Body:
The desktop bot retrieved the following data from the ERP system:
Customer: @{items('Apply_to_each')?['Title']}
Report Date: @{formatDateTime(utcNow(), 'yyyy-MM-dd')}
Total Revenue: $@{outputs('Run_desktop_flow')?['body/TotalRevenue']}
Transaction Count: @{outputs('Run_desktop_flow')?['body/TransactionCount']}
Status: @{outputs('Run_desktop_flow')?['body/StatusMessage']}
Tip
When referencing desktop flow outputs in expressions (rather than dynamic content tokens), use the format outputs('ACTION_NAME')?['body/ExternalVariableName'] where ExternalVariableName is the external name with spaces removed. So "Total Revenue" becomes body/TotalRevenue. This is easy to get wrong — use the dynamic content picker wherever possible, and only drop to raw expressions when you need to manipulate the value.
Text, numbers, and booleans are straightforward. But what if you need to return a whole table of records from your desktop flow? Say the ERP shows a list of individual transactions, and you want to send all of them back to the cloud.
Power Automate Desktop supports DataTable as an output type. When a DataTable is returned to the cloud flow, it arrives as a JSON array of objects, where each object's keys are the DataTable column names.
Here's how to build and populate a DataTable output in PAD:
// Define DataTable with columns
Create new data table: TransactionTable
Column names: TransactionID, Amount, Category, PostedDate
// Loop through extracted transactions from ERP
FOR EACH Transaction IN ScrapedTransactions
Add row to data table: TransactionTable
Row values:
TransactionID = %Transaction.ID%
Amount = %Transaction.Amount%
Category = %Transaction.Category%
PostedDate = %Transaction.Date%
END FOR
// TransactionTable is our Output variable - it gets returned automatically
In the cloud flow, the TransactionTable output arrives as an array. You can use an Apply to each loop to iterate over every row, accessing column values like items('Apply_to_each')?['TransactionID'].
For a deeper dive into how variables, lists, and DataTables work inside PAD itself, Variables, Lists, and Data Tables in Power Automate Desktop: A Complete Practitioner's Guide covers the full mechanics of building and manipulating these structures.
Warning
DataTables with thousands of rows will cause the desktop flow action to time out or hit payload limits. If you're returning large datasets, consider having the desktop flow write results to a SharePoint list, SQL database, or CSV file, and only return a summary or file path as the output variable. Use the cloud flow to then process the stored data separately.
You've seen both modes mentioned. Let's be concrete about the operational differences, because they affect how you architect the entire solution.
Attended mode:
Unattended mode:
The cloud flow action is identical for both — you just change the Run Mode dropdown. The infrastructure underneath is what differs.
For organizations running multiple unattended bots, machine groups with load balancing become essential. If your cloud flow sends 50 desktop flow requests in a scheduled batch and you only have one machine, they'll queue. With a group of 5 machines, they run in parallel (5 at a time), dramatically reducing total batch time.
Let me show you a complete architecture for the ERP reporting scenario we've been building. This is the kind of pattern you'd actually deploy in production.
Cloud Flow: "Daily ERP Revenue Report"
IsActive = trueFailedCustomers (Array) — to track failuresTitle from SharePoint itemformatDateTime(utcNow(), 'yyyy-MM-dd')IncludePending from SharePoint item
b. Condition: Is Status Message equal to "Success - Data retrieved..."?FailedCustomers arrayFailedCustomers array empty?This pattern gives you full observability: you know exactly which customers succeeded, which failed, and why (via the StatusMessage output). The operations team gets alerted only when intervention is needed.
Key insight
Notice the "Apply to each" is set to sequential processing in this example. If your machine group has enough capacity and your desktop flow is safe to run in parallel, you can switch to concurrent execution with a concurrency limit matching your machine count. But concurrent desktop flow runs against the same application (like a shared ERP instance) can cause race conditions, conflicting window focus, or login conflicts. Sequential is safer as a default; move to parallel only after you've validated that your target application handles concurrent sessions cleanly.
If you want to explore parallel execution patterns in the cloud flow side of this architecture, Implementing Parallel Branching and Concurrency Control in Power Automate goes deep on exactly this problem.
Let's build a concrete, working integration from scratch. In this exercise, you'll create a desktop flow that accepts a website URL and a search keyword, navigates to the site, performs a search, and returns the number of results found. A cloud flow will trigger it daily and log the result to SharePoint.
Open Power Automate Desktop and create a new flow called "Web Search Result Counter".
Define Input Variables:
SearchURL | External: "Search URL" | Type: Text | Default: https://wsd.example.comKeyword | External: "Keyword" | Type: Text | Default: Power AutomateDefine Output Variables:
ResultCount | External: "Result Count" | Type: NumberExecutionStatus | External: "Execution Status" | Type: TextFlow logic:
// Error handling wrapper
ON ERROR
Set variable: ExecutionStatus = "Failed: " + %LastError%
STOP FLOW
END ON ERROR
// Launch browser and navigate
Launch new Microsoft Edge: URL = %SearchURL%
Wait for web page to load
// Enter search term
Populate text field on web page: SearchInput field
Text: %Keyword%
Press key: Enter
// Wait for results page
Wait for web page to load
Wait: 2 seconds
// Extract result count from results page
Get text from web page element: ResultCountElement -> RawCount
// Parse the number (e.g., "About 1,240 results" -> 1240)
// Use regex or text manipulation
Extract with regex: Text = %RawCount%, Pattern = "\d[\d,]*" -> MatchedText
Replace text: MatchedText replace "," with "" -> CleanCount
Convert text to number: CleanCount -> ResultCount
// Set success status
Set variable: ExecutionStatus = "Success"
// Close browser
Close web browser
For a deeper look at web automation techniques in PAD, including how to reliably target elements on dynamic pages, Web Automation in Power Automate Desktop: Browser Actions, Form Filling, and Data Extraction covers the full toolkit.
https://yoursite.com/searchPower Automate DesktopExecution Status equals SuccessPower Automate DesktopResult Count (dynamic content from desktop flow)formatDateTime(utcNow(), 'yyyy-MM-dd')Web Search Bot FailedStatus: + Execution StatusRun the cloud flow manually from the designer. Watch the desktop flow execute on your machine (in attended mode, you'll see the browser launch). After it completes, check the cloud flow run history to see the output values returned. Confirm the SharePoint item was created with the correct count.
Symptom: Cloud flow fails immediately at the desktop flow action with a connection error.
Cause: The machine agent isn't running, the machine is offline, or the connection in the cloud flow points to a machine that no longer exists.
Fix: On the target machine, open Power Automate Desktop and verify the machine is shown as "Active" in the machine settings. In Power Automate cloud, go to Monitor > Machines and confirm your machine shows "Active." Restart the machine runtime service if needed.
Symptom: After selecting the desktop flow in the cloud action, the input fields don't show up or show an older version.
Cause: The desktop flow was modified in PAD but not saved, or the cloud flow action is cached.
Fix: Save the desktop flow in PAD. In the cloud flow, remove the desktop flow action entirely and re-add it, then reselect the desktop flow. The inputs should refresh. Also check that your input variables have the direction set to "Input" (not "Output").
Symptom: Desktop flow runs successfully, but all output variables come back as null or empty in the cloud flow.
Cause: The flow finished before reaching the lines that set the output variables — usually due to an early exit, a missing error handler, or the flow branching down a path that skips the variable assignments.
Fix: Add logging at the end of your desktop flow. Use PAD's built-in logging to write output variable values to a file just before the flow ends. If values are present in the log but absent in cloud outputs, there's a serialization issue — try changing the variable type or avoiding special characters in text outputs. For debugging cloud flow behavior, Using Power Automate Run History and Flow Checker to Debug and Fix Failing Flows shows you how to inspect the exact payload exchanged.
Symptom: Cloud flow fails with "Invalid type" or the desktop flow receives garbled input.
Cause: You're passing a Text value from the cloud into a Number input variable in PAD (or vice versa).
Fix: Check the data type of each input variable in PAD against what the cloud flow is actually sending. Use the float() or int() expressions in the cloud flow to explicitly cast values before passing them. For Boolean inputs, ensure you're sending actual boolean dynamic content, not the string "true".
Symptom: Cloud flow times out after ~10 minutes even though the desktop flow is still running.
Cause: The default timeout for the "Run desktop flow" action is 10 minutes. Complex automations — like processing hundreds of records in a loop — can exceed this.
Fix: In the desktop flow action settings (the "..." menu), look for the timeout setting and increase it. You can set it up to 24 hours for unattended flows. Alternatively, redesign the desktop flow to process smaller batches and have the cloud flow call it in a loop — each call processes a chunk. This also makes the automation more resilient to failures, since a failed chunk doesn't kill the entire run. This is a natural use case for subflows and reusable logic inside PAD to chunk processing cleanly.
Symptom: Cloud flow fails with a message about no machines being available in the machine group.
Cause: All machines in the group are offline, already running another flow, or the group is empty.
Fix: Check machine status in Monitor > Machines. Ensure machines are powered on and the runtime agent is running. For high-volume scenarios, add more machines to the group. Consider adding retry logic in the cloud flow so it waits and retries if machines are temporarily busy — the error handling and retry patterns article covers exactly this scenario for cloud flows.
You now have a complete picture of how cloud flows and desktop flows collaborate. The key concepts to internalize:
The contract is the variables. Input and output variables, defined in PAD with correct types and external names, are the API surface of your desktop flow. Design them thoughtfully — they're harder to change later once other flows depend on them.
The mode determines the infrastructure. Attended runs are simpler but require human presence. Unattended runs are the foundation of true RPA but require more setup, correct licensing, and a Windows machine configured appropriately.
Outputs enable real integration. A desktop flow that just clicks buttons and returns nothing is a dead end. Desktop flows that return structured data — especially DataTables — become genuine data sources that feed the rest of your automation ecosystem.
Error paths must set outputs. Design your desktop flow so every possible exit — success, graceful failure, unexpected crash — results in meaningful output variables being set. Your cloud flow should never have to guess whether the bot succeeded.
For your next steps in building production-grade RPA integrations, consider these directions:
The bridge between cloud and desktop is where the real automation value lives for most enterprises. You now have everything you need to build it properly.