Logic apps: Create dummy PDFs with the SharePoint REST API

In one of our projects, we needed a way for users to generate a dummy PDF for a SharePoint document on demand. Not a copy of the original file, but a clearly identifiable placeholder PDF containing demo text such as “This is a dummy PDF”, while still preserving all relevant SharePoint metadata. This approach allowed users to work with a PDF representation without exposing or duplicating the original content.

Security and governance were important requirements. The dummy PDF had to be created using the SharePoint REST API, allowing a Logic App to authenticate via Managed Identity. This avoids client secrets or user credentials and results in a clean, secure, and fully automated setup.

The process is simple. A user sets a SharePoint column like “Create dummy PDF” to Yes, which triggers a Logic App. The Logic App creates the PDF via the REST API and then copies the metadata from the original document to the dummy PDF. In this blog, I’ll explain how this works, how to set values for different SharePoint column types using API calls, how to find the correct __metadata type for a document library and how to find the ServerRelativeUrl.

How to find the metadata type for a SharePoint document library

The metadata type for the SharePoint list is needed to update the properties of the created PDF file.

    • To get the metadata type, we need to find what is called the ListItemEntityTypeFullName of the SharePoint document library.
    • This can be done using the following REST API call.
    • Change the domain, site and document library name and use the API call in a browser.
    https://[domain].sharepoint.com/sites/[site]/_api/web/lists/getbytitle('[Document library name]')?$select=ListItemEntityTypeFullName
    
    https://demo.sharepoint.com/sites/demosite/_api/web/lists/getbytitle('Documenten')?$select=ListItemEntityTypeFullName
    • Look for the ListItemEntityTypeFullName and save the value.
    • In my example the value is: SP.Data.Gedeelde_x0020_documentenItem

    How to find the ServerRelativeUrl

    The server relative URL of the document library is required for the creation of the PDF file. I noticed that this URL is not always the same as the URL you see in the browser. With this API call you can find the correct value.

    • To get the Server Relative URL, we need to find what the ServerRelativeUrl of the SharePoint document library.
    • This can be done using the following REST API call.
    • Change the domain, site and document library name and use the API call in a browser.
    https://[domain].sharepoint.com/sites/[site]/_api/web/lists/getbytitle('[document library name]')?$select=RootFolder/ServerRelativeUrl&$expand=RootFolder
    
    https://demo.sharepoint.com/sites/demosite/_api/web/lists/getbytitle('documenten')?$select=RootFolder/ServerRelativeUrl&$expand=RootFolder
    • Look for the ServerRelativeURL property and save the value.
    • In my example the value is: /sites/demo/Documenten.

    Create a PDF file with SharePoint REST API

    • Add an HTTP action to you logic app.
    • Use the following API call to create a PDF file.
    • Change the domain, site, set the correct Server Relative URL, provide a file name.
    https://[domain].sharepoint.com/sites/[site]/_api/web/GetFolderByServerRelativeUrl('/sites/[site]/[document library]')/Files/add(url='[file name].pdf',overwrite=false)
    • Set the Method to POST.
    • Set a header called Accept to application/json;odata=verbose.
    • Set a header called Content-Type to application/pdf.
    • Set the body with the following code. If needed, change the content value to match your requirements.
    base64ToBinary('JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCA2MTIgNzkyXQogICAvQ29udGVudHMgNCAwIFIgL1Jlc291cmNlcyA8PCAvRm9udCA8PCAvRjEgNSAwIFIgPj4gPj4gPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0xlbmd0aCA0NCA+PgpzdHJlYW0KQlQKL0YxIDI0IFRmCjEwMCA3MDAgVGQKKER1bW15IGRvY3VtZW50LikgVGoKRVQKZW5kc3RyZWFtCmVuZG9iago1IDAgb2JqCjw8IC9UeXBlIC9Gb250IC9TdWJ0eXBlIC9UeXBlMSAvQmFzZUZvbnQgL0hlbHZldGljYSA+PgplbmRvYmoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmCjAwMDAwMDAwMTAgMDAwMDAgbgowMDAwMDAwMDYwIDAwMDAwIG4KMDAwMDAwMDExNyAwMDAwMCBuCjAwMDAwMDAyNzMgMDAwMDAgbgowMDAwMDAwNDE4IDAwMDAwIG4KdHJhaWxlcgo8PCAvUm9vdCAxIDAgUiAvU2l6ZSA2ID4+CnN0YXJ0eHJlZgo1MzAKJSVFT0Y=')
    • Set the Authentication to Managed identity.
    • Select the correct managed identity.
    • Set the Audience to your SharePoint domain.

    Update the PDFs file properties using the SharePoint REST API

    • Add an HTTP action to you logic app.
    • Use the following API call to update the properties of the PDF file.
    • Change the domain, site, document library name and the correct item ID.
    https://[domain].sharepoint.com/sites/[site]/_api/web/lists/getbytitle('[document library]')/items([item ID])
    
    https://demo.sharepoint.com/sites/demosite/_api/web/lists/getbytitle('Documenten')/items(1)
    • Set the Method to POST.
    • Set a header called Accept to application/json;odata=verbose.
    • Set a header called Content-Type to application/json;odata=verbose.
    • Set a header called X-HTTP-Method to MERGE.
    • Set a header called IF-MATCH to *
    • The basic code for the body is as follows, but you will need to add in your specific SharePoint properties. In the paragraph JSON Examples for property types, you can see the syntax required for different types of SharePoint columns.
    • Set as the type of the __metadata the ListItemEntityTypeFullName of the document library.
    {
      "__metadata": {
        "type": "SP.Data.Gedeelde_x0020_documentenItem"
      },
      "Title": "G.104612",
      "ContentTypeId": "0x010100000AAAAAA111111D9C039461496B7308010097DF96DFF524EA4E900ED55E769ECEAF"
    }
    • Set the Authentication to Managed identity.
    • Select the correct managed identity.
    • Set the Audience to your SharePoint domain.

    JSON Examples for property types

    SharePoint uses different column types to store data. Depending on the column type, different JSON syntax is required. Always use the internal name of the SharePoint column.

    Setting a string: "Title": "This is my string"
    Setting a number: "Number": 1
    Setting a choice field: "ChoiceField": "Choice 1",
    Setting a date field: "Date": "2026-04-20T00:00:00Z"
    Setting a Lookup where multiple values are allowed: "LookupId": { "results": [ 1, 3 ] }
    Setting a Lookup where multiple values are not allowed: "LookupId": 1
    Setting an empty date field: "Date": null
    Setting an empty string: "Title": ""
    Setting an empty number: "Number": null
    Setting a choice field to empty: "CreateDummyPDF": null
    Setting a Lookup where multiple values are allowed to empty: "LookupId": { "results": [] }
    Setting a Lookup where multiple values are not allowed to empty: "LookupId": null

    Note for lookup fields you need the field name plus Id.

    Flow: Set permissions using REST API without app permissions

    In my last blog post Flow: Set permissions using REST API, I explained how to use the HTTP action and the app permissions. With the HTTP action you can start web services from SharePoint but also from other solutions.
    In this blog post I will explain an easier way of using the SharePoint REST API. This setup does not require setting the app permissions. We can simply use the action Send an HTTP request to SharePoint.

    Creating the Flow

    • First, we will break the inheritance.
    • Create a Flow with the required trigger.
    • Add the Initialize variable.
    • Rename it to Initialize variable - User Principle ID.
    • Set the name to User Principle ID.
    • Set the type to String.
    • Add the Send an HTTP request to SharePoint action.
    • Change the name to Send an HTTP request to SharePoint - Break inheritance.
    • Select or set the correct Site Address.
    • Set the Methode to POST.
    • The URI is different for every item/list you want to manipulate.
    • _api/lists/getbytitle(‘<list display name>’)/items(<ITEM ID>)/breakroleinheritance(true)
    • Set the Headers as follows.
    • The key is Accept
    • The value is application/json;odata=verbose
    • Now we will look up the user's information.
    • Add the Send an HTTP request to SharePoint action.
    • Change the name to Send an HTTP request to SharePoint - Get User Info.
    • Select or set the correct Site Address.
    • Set the Methode to GET
    • The URI is different for every item/list you want to manipulate.
      _api/web/siteusers/getbyemail('<email address>')
    • Set the Headers as follows.
    • The key is Accept
    • The value is application/json;odata=verbose
    • Add the Parse JSON action.
    • Set the Content to Body (the result of the Get User info action).
    • Change the name to Parse JSON - Get User Principal ID.
    • Use the following Schema to parse the JSON. This will give you more than you will not for this example. I add this Schema, so you have access to more information if required.
    {
        "type": "object",
        "properties": {
            "d": {
                "type": "object",
                "properties": {
                    "Alerts": {
                        "type": "object",
                        "properties": {
                            "__deferred": {
                                "type": "object",
                                "properties": {
                                    "uri": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    },
                    "Groups": {
                        "type": "object",
                        "properties": {
                            "__deferred": {
                                "type": "object",
                                "properties": {
                                    "uri": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    },
                    "Id": {
                        "type": "integer"
                    },
                    "IsHiddenInUI": {
                        "type": "boolean"
                    },
                    "LoginName": {
                        "type": "string"
                    },
                    "Title": {
                        "type": "string"
                    },
                    "PrincipalType": {
                        "type": "integer"
                    },
                    "Email": {
                        "type": "string"
                    },
                    "Expiration": {
                        "type": "string"
                    },
                    "IsEmailAuthenticationGuestUser": {
                        "type": "boolean"
                    },
                    "IsShareByEmailGuestUser": {
                        "type": "boolean"
                    },
                    "IsSiteAdmin": {
                        "type": "boolean"
                    },
                    "UserId": {
                        "type": "object",
                        "properties": {
                            "__metadata": {
                                "type": "object",
                                "properties": {
                                    "type": {
                                        "type": "string"
                                    }
                                }
                            },
                            "NameId": {
                                "type": "string"
                            },
                            "NameIdIssuer": {
                                "type": "string"
                            }
                        }
                    },
                    "UserPrincipalName": {
                        "type": "string"
                    }
                }
            }
        }
    }
    
    • Add the Set variable action.
    • Change the name to Set variable - User Principle ID.
    • Select by name the variable User Principle ID.
    • Set the value to ID (the ID output from Parse JSON - Get User Principal ID)
    • Now we will grant a user permissions, I am granting the challenger (property of the item) contribution access.
    • Add the Send an HTTP request to SharePoint action.
    • Change the name to Send an HTTP request to SharePoint - Grant Contribute permissions.
    • Select or set the correct Site Address.
    • Set the Methode to POST.
    • The URI is different for every item/list you want to change.
      <site url>/_api/lists/getbytitle(‘<list display name>’)/items(<ITEM ID>)//roleassignments/addroleassignment(principalid='<d.ID>’roleDefId=1073741827)
    • The id 1073741827 stands for contributor, in my blog post SharePoint: Get the Role ID you can read more about role id’s.
    • Your Flow will now look like this.

    Flow: Set permissions using REST API

    For a customer I am creating Microsoft Flows to support various business processes. One Flow controls the process of reviewing and approving vital business documents. These documents are so important that during the day to day business the reviewers and approvers are unable to edit these documents. Only when the documents are in review a select few are allowed to edit them.

    Microsoft Flow contains many default actions for all sorts of tasks, but
    during the writing of this blog post there is no default action that can set permissions or break the inheritance.

    In this blog post I will explain how set custom permissions, break the inheritance and restore the inheritance using the REST API.

    Register app and grant app permissions

    For the Flow to work we will need to Register an App and grant it permissions. During the registration of the App we will receive various id's we will use in the Flow.

    • Navigate to the app registration page on the SharePoint site.
    https://<tenant>.sharepoint.com/_layouts/15/appregnew.aspx
    
    • Generate a Client ID and Client Secret.
    • Set the Title to Flow Web Service
    • Set App Domain to www.localhost.com
    • Set Redirect URI to https://www.localhost.com
    • Save the generated Client Id and Client Secret.
    • Navigate to the Grant permissions to an app page. This must be done by browsing to the appinv.aspx page of the site.
    http://<hostname>/<the Site Collection>/_layouts/15/appinv.aspx
    
    • Fill in the Client Id and click on Lookup
    • Now we will grant the App full control access to the site collection.
    • Set the following XML as the Permission Request XML
    <AppPermissionRequests>
        <AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="FullControl" />
    </AppPermissionRequests>
    
    • Click on Create.
    • A pop-up screen will appear asking to Trust the App, click on Trust It.
    • The App has been granted permissions and can be used in the Flow.

    Creating the Flow

    • Create a Flow with the required trigger.
    • Add two initialize variable actions for the Token type and Access token.
    • Add the HTTP action to acquire the access token and type.
    • Set the name to HTTP - Request access token and type
    • Set the Method to POST.
    • The URI is different for every tenant, you will need to your tenant id.
    • https://accounts.accesscontrol.windows.net/<tenant ID>/tokens/OAuth/2
    • Set the Headers as follows.
    • The key is Content-Type
    • The value is application/x-www-form-urlencoded
    • For the body you will need the tenant ID, client_credentials, client_id and client_secret.
    • grant_type=client_credentials&client_id=
      <Client ID>@<Tenant ID&client_secret=<Encoded Client Secret>&resource=00000003-0000-0ff1-ce00-000000000000/<tenant>.sharepoint.com@<Tenant ID>
    • Add a Parse JSON action.
    • Set the content to the BODY (result) of the HTTP - Request access token and type action.
    • Use the following Schema to parse the JSON.
    {
        "type": "object",
        "properties": {
            "token_type": {
                "type": "string"
            },
            "access_token": {
                "type": "string"
            }
        }
    }
    
    • Set the variable Token type and Access token using the results of the parsed JSON.
    • Now you can break the inheritance, add a HTTP action.
    • Set the name to HTTP - break inheritance.
    • Set the Method to POST.
    • The URI is different for every item/list you want to change.
    • <site url>/_api/lists/getbytitle('<list display name>')/items(<ITEM ID>)/breakroleinheritance(true)
    • Set the Headers as follows.
    • The key is Authorization
    • The value is the Token type and Access token variable. Make sure to add a space between the variables.
    • The key is Accept
    • The value is application/json;odata=verbose
    • Now you can grant an user permissions, I am granting the challenger (property of the item) contribution access.
    • Add a HTTP action, with this action we will get the users information.
    • Set the name to HTTP - Get User info
    • Set the Method to GET
    • The URI is different for every location where you want to get the information from.
      <site url>/ _api/web/siteusers/getbyemail('<Challengers email>')
    • Set the same Headers as before.
    • Add the Parse JSON action, name is Parse JSON - Get User Principal ID
    • Set the content to the BODY (result) of the
      HTTP - Get User info action
    • Add the following Schema to the Use sample payload to generate the schema option.
    • Make sure to enter your own account details.
    {
      "d": {
        "results": [
          {
            "Id": 9,
            "LoginName": "i:0#.f|membership|<youraccount>",
            "Title": "<your name>",
            "PrincipalType": 1,
            "Email": "<youremail>",
            "IsSiteAdmin": true
          },
          {
            "Id": 14,
            "LoginName": "i:0#.f|membership|<youraccount>",
            "Title": "Example Title",
            "PrincipalType": 1,
            "Email": "<youremail>",
            "IsSiteAdmin": false
          }
      
        ]
      }
    }
    
    • Add a HTTP action, set the name to HTTP - Grant contribute permissions
    • Set the Method to POST
    • The URI is different for every item/list you want to change.
      <site url>/_api/lists/getbytitle('<list display name>')/items(<ITEM ID>)//roleassignments/addroleassignment(principalid='<d.ID>'roleDefId=1073741827)
    • The id 1073741827 stands for contributor, in my blog post SharePoint: Get the Role ID you can read more about role id's.
    • Set the same Headers as before.
    • Now you can reset the inheritance if needed, add a HTTP action
    • Set the name to HTTP - Reset Role Inheritance
    • Set the Method to POST
    • The URI is different for every item/list you want to change.
      <site url>/_api/lists/getbytitle('<list display name>')/items(<ITEM ID>)/resetroleinheritance
    • Set the same Headers as before.
    • Your Flow will now look like this.