Introduction

Recently, I was building an agent in Copilot Studio (Classic) that needed to accept files from chat and save them later in the flow. The common approach is to pass a single file straight to a tool call using System.Activity.Attachments - but you can only pass one file, not the array.

Single attachment example in Copilot Studio

That works for simple cases, but it introduces two problems:

  • It does not handle multiple files. If you have more than one file in a single message, you can only save the first or last one. The agent will also only call the tool once
  • System.Activity.Attachments only exists on the current activity, so attachments can be lost if you perform other actions first

The main reason for this issue is the agent is using the Bot Framework’s System.Activity object, which is designed to last for a single turn. Once the turn is processed, the attachments are no longer available.

In this post, I will show a simple pattern that avoids both issues. The goal is to capture uploaded files immediately, store them in a global variable, and save them later when your process is ready.

Step 1: Create a topic for each incoming message

First, create a topic Add Attachments that runs when the user message contains attachments.

In this example, the topic checks whether attachments are present and stores them in a global variable for later use.

kind: AdaptiveDialog
modelDescription: Capture message attachments into a global queue.
beginDialog:
  kind: OnRecognizedIntent
  id: main
  condition: =!IsEmpty(System.Activity.Attachments)
  intent: {}
  actions:
    - kind: SetVariable
      id: setVariable_BrT3By
      variable: Global.Attachments
      value: |-
        =System.Activity.Attachments

    - kind: SendActivity
      id: sendActivity_OJC34y
      activity: |-
        {"Added " & CountRows(System.Activity.Attachments) & " attachment(s). " & CountRows(Global.Attachments) & " total attachment(s) now queued."}

inputType: {}
outputType: {}

You should now see a confirmation in chat every time files are uploaded.

Chat confirmation showing queued attachment count after uploading files

Step 2: Keep attachments queued until needed

At this point, do not upload files yet.

Run the rest of your process first, for example:

  • Extract and validate key values.
  • Ask the user to confirm details.
  • Create or update a target record.

The key point is that attachments stay available in Global.Attachments while this happens.

Step 3: Create a topic for saving attachments

Next, create a second topic Save Attachments to save the queued attachments.

In my example, I used a RecordId input so files are saved into an ID-based folder structure that matches a record in a SharePoint list.

That input is not required for this pattern. If you do not need ID-based grouping, you can save files without RecordId.

In this example flow:

  • If RecordId is missing, end early with a clear message.
  • If attachments are missing, end early with a clear message.
kind: AdaptiveDialog
modelDescription: Upload queued attachments to a target location by record ID.
beginDialog:
  kind: OnRecognizedIntent
  id: main
  intent: {}
  actions:
    - kind: ConditionGroup
      id: ensureRecordId
      conditions:
        - id: recordIdPresent
          condition: =!IsBlank(Topic.RecordId)
          actions:
            - kind: ConditionGroup
              id: ensureAttachments
              conditions:
                - id: hasAttachments
                  condition: =CountRows(Global.Attachments) > 0
                  actions:
                    - kind: Foreach
                      id: foreach_UrrGeW
                      items: =Global.Attachments
                      value: init:Topic.Attachment
                      index: init:Topic.LoopIndex1
                      actions:
                        - kind: InvokeConnectorAction
                          id: invokeConnectorAction_RdAjBY
                          input:
                            binding:
                              dataset: https://<tenant>.sharepoint.com/sites/<site>
                              file: =Topic.Attachment.Content
                              folderPath: =Concatenate("/Shared Documents/", Topic.RecordId, "/")
                              name: =Topic.Attachment.Name

                          output:
                            kind: SingleVariableOutputBinding
                            variable: Topic.CreateFile

                          connectionReference: <sharepoint-connection-reference>
                          connectionProperties:
                            name: <sharepoint-connection-reference>
                            mode: Maker

                          operationId: CreateFile

                    - kind: SendActivity
                      id: uploadSummary
                      activity: '{"Uploaded " & Text(CountRows(Global.Attachments)) & " attachment(s) for record " & Topic.RecordId & "."}'

              elseActions:
                - kind: SendActivity
                  id: noAttachments
                  activity: No saved attachments were found to upload.

      elseActions:
        - kind: SendActivity
          id: missingId
          activity: Record ID is required before upload.

        - kind: EndDialog
          id: endMissingId

inputType:
  properties:
    RecordId:
      displayName: Record ID
      description: ID used to group and upload attachments
      type: String

outputType: {}

You should now have a reusable save topic that can upload all queued files.

Note: Replace the SharePoint action with your own or populate with your own values

Step 4: Ensure your instructions reference the topics

Finally, ensure your instructions reference the two topics in the correct order:

  1. Run Add Attachments on each incoming message.
  2. Run Save Attachments only after the record ID is available.

Basic example instructions

1. On every message, run Add Attachments first.
2. Provide a summary of the file to the user. Ask user if they want the file saved. If yes, generate a RecordId as a whole number from 1 to 100 and run Save Attachments.

Recap

We have covered a simple pattern to capture and save attachments in Copilot Studio. This gets around the limitations of System.Activity.Attachments and allows you to queue multiple files for later processing. The full conversation is here, with the resulting files appearing in their destination.

Full chat conversation showing attachments queued and saved after providing a record IDSaved attachments appearing in the destination SharePoint folder

Before you ask - as of July 2026 - there is no way to reliably do this same pattern in the new Copilot Studio experience. The new experience does not support global/system variables, topics or anyway to reliably parse a file and pass to a tool.