If you’re building Agent experiences with Copilot Studio, there may come a time when you need to handle file uploads from users, like documents, images, or forms, and send those files to Power Automate for further processing or storage. However, when it comes to send files uploaded by the user as parameters to a Power Automate flow, the solution isn’t always straightforward.
For example, the When an agent calls a flow Power Automate trigger supports file inputs as parameters:

But if we try to use a variable with a File Content type as input to a flow that expects a file (Record type in Copilot Studio), it will result in an error, as the formats are not compatible:

In this blog, you will learn how to send files uploaded by the user from Copilot Studio to Power Automate.
Note: The solution proposed here was tested only with Agents in Microsoft Teams and embedded into a Website.
Identifying the files uploaded by the user in Copilot Studio
Let’s start by creating a new Topic in the Agent, where we prompt the user to upload one or more files (in red). The question is configured to expect a file (in green) and the user’s reply is saved to a variable named uploaded_file. Later in this same Topic, the Agent will simply thank the user for sharing the file (in blue):

When Copilot collects the uploaded file and stores it in a variable, , it may seem like we can use this same data as input for a Power Automate flow triggered by the Agent (check instructions for setting up this flow here). However, as mentioned earlier in this blog, we get an error when trying to do it, since the data types don’t match.
Sending files as JSON from Copilot Studio to Power Automate
To integrate file uploads with Power Automate, we can get the data related to the files uploaded by the user with the system variable Activity.ChannelData. this variable is an UntypedObject, we need to convert it to a supported format before sending it to Power Automate.
The easiest way to perform this transformation is to use the JSON() function, which will format the the UntypedObject data into a JSON string. Before it, we just need to set the expected format of the Power Automate trigger to Text:

And once we implement this change, we can pass the formula (in red) as input to the Power Automate flow within our Topic (in blue):

Extracting file content in Power Automate
For all the following steps, we will test the Agent directly in Microsoft Teams, since the data sent to Power Automate differs when we use the Copilot Studio test pane and a live Channel. The solution shared here was tested only with the Microsoft Teams and the Web app channels, and results may vary if using others.
The first thing to do in Power Automate is to convert the JSON string received from Copilot into a JSON object, and for that, we will use the json() function within the flow. If you are not familiar with both platforms, the fact that a function with the same name performs different transformations in each tool may be a bit confusing: while in Copilot Studio this function transforms any data type into a JSON string, in Power Automate it converts a string into an object.
As a parameter for this expression, you just have to pass the dynamic content corresponding to the text input from the trigger. For this example, we’re using a Compose to store this expression (in red):

Now we can save the flow and test it in a conversation with the Agent vin Microsoft Teams, uploading a PDF file:

After running the flow, if we check the raw outputs from the Compose, we will find an array of objects named OriginalAttachments (in yellow). This array includes one record for each uploaded file, and when you’re working with Microsoft Teams, there is also an extra element with an empty content property (highlighted in blue). The objects corresponding to files will contain properties for the file name with extension (in red), along with metadata about the content, including a URL for downloading the uploaded file (in green):

Since there is an extra object generated by default with an empty content property, we first have to filter out this element from the OriginalAttachments, and we can achieve it by using a Filter array action, passing the reference to this property as input (in blue), with the expression outputs(‘Compose_-_JSON’)[ ‘ OriginalAttachments ‘]. Then in the filter condition, we must use the empty() function to test if the content property has any value (in red). Keep the condition set to is equal to, and set the right-hand value to false. For the function (in red), we are using the expression empty(item()[‘content’]).

Next, we need to iterate through thefilteredarray using an Apply to each loop to download the file content from the JSON. We are passing the outputs from Filter array as input for the loop (highlighted in blue below), and allocating a HTTP action inside it, passing the downloadUrl property as parameter for URI (in red) using the expression item()[‘content’][‘downloadUrl’], and setting the Method parameter as GET (in green):

The output of the HTTP action will contain the content of the file uploaded by the user during the chat with the Agent:

Next step is to add a Create file action, setting the File Name as the name property from the current item in the loop (expression item()[‘name’]). For the File Content, we will use the $content property from the HTTP response body converted from base64 to binary (expression base64ToBinary(body(‘HTTP’)[‘$content’])):

IMPORTANT: the conversion from base64 to binary is required only for binary file types, such as .PDF, .XLSX, .JPEG and many others. If you are expecting the user to upload files with plain text types, such as .JSON, .HTML or .TXT, you can skip the conversion step and pass the body property from the HTTP action as input to the File content.
If you can’t predict which file types will be provided by the user, you can then use a more dynamic expression that relies on a condition to check if the HTTP response body includes the $content property (plain text file types won’t have it), and then decide the File Content input depending on the presence of this key. As you can see in the example below, we’re using the expression if(contains(body(‘HTTP’), ‘$content’), base64ToBinary(body(‘HTTP’)?[‘$content’]) , body(‘HTTP’)):

The flow design is ready, but before testing it, we just need to change the Run-only settings, so we won’t ask for the user for authentication when creating the file in the SharePoint Document Library. In the flow details page, find this setting on the right-side of the page and click Edit:

And then you can set the SharePoint connection to anything different from Provided by run-only user:

Testing the solution
Let’s now send the file again:

The flow ran successfully, having only one iteration in the Apply to each:

And the respective file was created in the SharePoint Folder:

We can also test it with two other files:

And they will also be loaded to the SharePoint Folder:

Conclusion
In this blog, we saw how to manage files uploaded by the user in chats with an Agent created with Copilot Studio, by sending them to Power Automate. Let us know what do you think about this blog in the comment section, and don’t forget to check our website, our YouTube Channel or connect on LinkedIn!
