Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save zplume/9f4c1a658517802701deff3473f23a60 to your computer and use it in GitHub Desktop.
Save zplume/9f4c1a658517802701deff3473f23a60 to your computer and use it in GitHub Desktop.

Moving Files with SharePoint Online REST APIs in Microsoft Flow

About

This is an overview of how to move files with Microsoft Flow using the SharePoint - Send an HTTP request to SharePoint action and SharePoint Online REST APIs.

These SharePoint Online REST APIs can be used outside of Flow, but you will need to handle authentication and provide an X-RequestDigest header with any POST requests (as with any SP REST POST request).

In the case of Flow's SharePoint - Send an HTTP request to SharePoint action, authentication is handled for you and you do not need to provide the X-RequestDigest header.

Why

This is something I came across while building a two-stage approval workflow with Flow, which had the following requirements:

  • Allow users to submit a document for approval (using Flow's SharePoint - For a selected item trigger)
  • When triggered, the Flow should create a copy of the document in a Stage 1 Approval folder (in the same library) and start an approval process on the approval copy of the document via the Approvals - Start an approval action
    • If approved at stage 1, move the approval copy of the document to a Stage 2 Approval folder (in the same library) and start another approval process
    • If rejected, move the approval copy of the document to a Rejected folder
  • If approved at stage 2, move the approval copy of the document to an Approved folder

While Flow already had a SharePoint - Copy file action, there wasn't (at least when I wrote this) a SharePoint - Move file action.

At this point I started looking at using the SharePoint - Send an HTTP request to SharePoint action (the most useful Flow SharePoint action?) with SharePoint Online REST APIs to move files.

Several samples online (including Microsoft documentation) suggest the following API call (or some variation) to move a file:

https://tenant.sharepoint.com/_api/web/getFileByServerRelativeUrl('/Shared Documents/FileName.docx')/moveTo(newurl='/Shared Documents/FolderName/FileName.docx',flags=1)

In the example above, flags=1 means overwrite an existing file at newurl if there is one, rather than returning an error.

This was fine in early development, but with more testing I found the following issues:

  • Long file names / paths can quickly exceed URL limits causing the API request to fail
  • ' characters in file/folder names are problematic

I spent some time trying to work around these issues, but was asking myself why I was battling with sending POST data in the request URL that should really (in my humble opinion) be included in the POST body instead.

And then I started thinking about how SharePoint Online's modern document library UI has a 'Move To' button...

Inspecting SharePoint API traffic

If you're curious about how SharePoint Online's modern UI uses SharePoint REST APIs, you can inspect the Network traffic sent from the SharePoint page using either the Network tab of your browser developer tools, or a debugging proxy like Telerik's excellent (and free) Fiddler.

This can be a useful technique for discovering as-yet undocumented APIs or how to use APIs that are documented but not explained clearly enough with examples.

In this post I'll focus on using the Network tab of Chrome's developer tools, but the same principals should apply to other browser development tools.

To only show API requests, you'll want to filter traffic shown in the Network tab by clicking on the XHR heading. You can additionally filter by a text value in the 'Filter' text box.

SP.MoveCopyUtil in the modern UI

If you navigate to a SharePoint Online document library that's configured to show the modern UI and (while inspecting browser network traffic) use the 'Move To' button to move a file to a different folder in the same library, you'll see the following REST request:

https://tenant.sharepoint.com/_api/SP.MoveCopyUtil.MoveFileByPath()

This endpoint accepts source and destination URLs in the request body rather than the request URL, which means it doesn't have the drawbacks of the getFileByServerRelativeUrl('/from/')/moveTo(newUrl='/to/') approach. 🎉

If you try and move a file to a folder that already contains a file with that name, the API request will return an error (HTTP 400) and you'll see the prompt A file with this name already exists with an option to replace the existing one. If you click Replace, the request is sent again with an additional parameter:

https://tenant.sharepoint.com/_api/SP.MoveCopyUtil.MoveFileByPath(overwrite=@a1)?@a1=true

If you inspect the request body (in Chrome this is via Headers > Request payload), you'll see something like the following JSON:

{
    "srcPath": {
        "__metadata": {
            "type": "SP.ResourcePath"
        },
        "DecodedUrl": "https://tenant.sharepoint.com/Shared Documents/FileName.docx"
    },
    "destPath": {
        "__metadata": {
            "type": "SP.ResourcePath"
        },
        "DecodedUrl": "https://tenant.sharepoint.com/Shared Documents/FolderName/FileName.docx"
    }
}

As you can see, the DecodedUrl values are absolute URLs that include the source and destination file names.

Recreating the SP.MoveCopyUtil.MoveFileByPath request

You can recreate this request in Flow using the Data Operations - Compose and SharePoint - Send an HTTP request to SharePoint actions, as follows (using the example JSON above as a starting point for the Data Operations - Compose action's Inputs value):

Moving a SharePoint file with Flow

Creating the request body in a separate Data Operations - Compose action makes debugging the Flow easier if something goes wrong with the request.

When configuring the SharePoint - Send an HTTP request to SharePoint action, you should specify the following values:

Properties

Property Value
Site Address The SharePoint site URL
Method POST
Uri _api/SP.MoveCopyUtil.MoveFileByPath(overwrite=@a1)?@a1=true
Headers See Headers table below
Body The Output of the Data Operations - Compose action

Note: swap out the Uri value for _api/SP.MoveCopyUtil.MoveFileByPath() if you don't want to overwrite an existing file in the destination.

Headers

Name Value
Accept application/json; odata=nometadata
Content-Type application/json; odata=verbose

Is that it?

Well, almost. If you only want to move files from one location in a document library to another location in the same document library, that's pretty much the end of the story.

But what about...

Copying files

If you click the Copy To button in the modern library UI while inspecting browser network traffic, you'll see the following request (or a variation thereof):

https://tenant.sharepoint.com/_api/SP.MoveCopyUtil.CopyFileByPath()

As with SP.MoveCopyUtil.MoveFileByPath, you can recreate this request in Flow with the SharePoint - Send an HTTP request to SharePoint action to copy files.

Metadata and version history

Move To - in the same library

When moving files from one location in a library to another location in the same library, the list item associated with the source and destination file is the same, meaning that version history and field values are automatically preserved, as you are only updating the file location within the library.

Move To - in another library, site or site collection

If you use the above approach to move a file from one library to another library, or even to another site or site collection, the list item associated with the source and destination file are not the same (think of it as a 'copy' followed by a 'delete' of the original file) and version history is not maintained.

In this case field values will be replicated from source to destination (if possible).

However, although it's possible to move files across site collections with SP.MoveCopyUtil.MoveFileByPath, if you move a file from one site collection to another via the modern UI's Move To button while looking at your browser's network traffic, you'll notice that it switches to a different set of APIs...

CreateCopyJobs and GetCopyJobProgress

_api/site/CreateCopyJobs
_api/site/GetCopyJobProgress

I've experimented a little with recreating the CreateCopyJobs and GetCopyProgress APIs in Flow, but these probably deserve their own post. So far I have noticed the following from inspecting network traffic while moving files from one site collection to another via the Move To button and recreating the calls from Flow:

  • The CreateCopyJobs API will return an error if fields from the source list are missing in the destination list, unless an AllowSchemaMismatch parameter in the request body is specified as true. This is matched by the UI prompting you of the same (missing fields), with a button to proceed anyway, which then resends the request with the AllowSchemaMismatch parameter set to true.
  • Version history is copied when using CreateCopyJobs with the IgnoreVersionHistory parameter set to false. If the parameter is set to true, the destination file will be version 1.0.
  • If the destination file already exists when the IsMoveMode parameter is set to true, the copy job will fail (see the Logs when inspecting the output of GetCopyJobProgress). There doesn't appear to be an 'overwrite' parameter in the CopyMigrationOptions class to allow overwrite on a move. This also happens with the UI Move To button - the following error message is displayed: A file or folder with this name already exists. Please rename the file or folder and try again.

API references for CreateCopyJobs and GetCopyJobsProgress

Hopefully someone finds the above useful 🙂

Updates

Date Update
22nd June 2018 Updated to reference 'SharePoint Online' rather than 'SharePoint', as the post content has only been tested in that context
22nd June 2018 Updated info on CreateCopyJobs and GetCopyJobProgress based on further testing
@amitpaple
Copy link

Hi @zplume, Firstly Thanks for sharing this wonderful blog, but just wanted to check if there is a way to move or copy Multiple Folder's/File's from one folder to another folder ?

@insuganhau
Copy link

I tried using this example to move files from Sharepoint Online to Sharepoint On-prem, but without success.
Do you know how to achieve it with Power Automate?

@viplavmulka
Copy link

The parameter exportObjectUris does not exist in method CopyFileByPath

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment