Microsoft List – Add Comment

If you are looking to automatically add comments to Microsoft List Items (or “SharePoint” List items) via Power Automate, you’ve come to the right place. In this article, I will show you how you can use the SharePoint Rest API to both post a comment and read ALL comments on a list item. Surprisingly, adding a comment is a single action, reading all comments into a convenient format when there is tagging involved, is rather complex as tagging is handled by a separate array.

The SharePoint REST API is well documented here as are the Microsoft List comment features, like the ability to tag and therefore trigger an email, notifying the user of a mention in a list comment. The email is a feature of Microsoft Lists and therefore does not depend on a Flow. If you create a comment on a list item and tag a colleague, they will be emailed with a link direct to the associated item.

Adding a comment

Via the “Send an HTTP Request to SharePoint” trigger, you simply select the site that you wish to post a comment to, and then via the URI, define the List and List Item. Below you can see that the List Name is defined as ‘Issue Tracker’, the item ID, in this case, is dynamic but is based on the unique item ID.

_api/web/lists/getbytitle('LISTNAME')/items('ITEMID')/Comments

The body of the action is based on the text and optional mentions, defined by multiple objects of an array. In the above, we see that the first email is referenced by object 0 i.e. @mention{0} because objects are defined by integers, starting from 0. Below is a sample payload for you to copy and use in your flow.

{
    "text": "This is a new item @mention{0}.  Please take a look ASAP!",
    "mentions": [
        {
            "email": "AnEmail@YourTenant.onmicrosoft.com"
        }
    ]
}

Possible use cases for this include tagging users in the progress of an approval flow or highlighting list items that meet certain criteria. You could even use adaptive cards to request a comment and further tag colleagues in the progress. A sample of comments on a list item can be seen below.

Microsoft List Comments

Read ALL comments

This is very much an identical process, albeit the body is empty and the HTTP API Method is GET. As before, make sure you send the List Name and Item ID via the URI and the API will return a body where the comment text and mentions are contained with a selection of nested arrays that you can see below.

Body payload of comments in Microsoft List

Turning this into something meaningful is worth a blog post of its own and it stumped me when producing the video demo, to be honest. Manipulating and re-purposing JSON is a good challenge though and so I came up with the following solution.

using a select to query the body of results
FROM: body('Send_an_HTTP_request_to_SharePoint')?['body']?['d']?['results']
TEXT: concat(formatdatetime(item()?['createdDate'],'dd-MM-yy HH:mm'),' ',item()?['text'])
MENTION: item()?['mentions']?['results']

With the body of the HTTP action as the source and using a Select Action, I have defined two keys, the Text and the Mentions (we will re-purpose in an apply to each step next). The expressions for both are supplied above. For the text, I concatenate the created date (and format it to the UK DateTime format) and combine it with the comments text. For the mentions, I grab the results array for use in the next step. I now end up with an array similar to below. Note that in each mention ” @mention{0&#125″ there is an incremental integer starting from 0, we use this last on.

"body": [
        {
            "Text": "11-09-21 19:58 Hi @mention{0} and @mention{1} ",
            "Mention": [
                {
                    "email": "emailuser1@mytenant.onmicrosoft.com",
                    "id": 10,
                    "loginName": "i:0#.f|membership|damien@abdndamodev.onmicrosoft.com",
                    "name": "DamoBird365"
                },
                {
                    "email": "emailuser2@mytenant.onmicrosoft.com",
                    "id": 17,
                    "loginName": "i:0#.f|membership|daveazure@abdndamodev.onmicrosoft.com",
                    "name": "Dave Azure"
                }
            ]
        }

Then using an apply to each on the body of the select action above, I work through each comment and re-purpose / re-build the mentions array as follows.

First, the Mentions Select Action is a select of the mention array for each comment and brings together an array of all names mentioned.

FROM: items('Apply_to_each')?['mention']
MAP: item()?['name']

The Split on Mention action is a compose using the split expression. Here I replace “@mentions&#123″ with an empty string ” and “&#125” with the string ‘ ||||SPLITME||||’. I then split on the new string with ‘SPLITME||||’ to give me a new array of objects that include an integer for the object and ||||. I’ve assumed that no one will ever comment with |||| or the phrase SPLITME. Please note that the last element is empty, but equally, it may contain the end of the comment which we need to capture later.

split(replace(replace(items('Apply_to_each')?['text'],'@mention{',''),'}','||||SPLITME||||'),'SPLITME||||')
an array of mention objects
An array of mentions for each comment (note the last element is empty)

The Select Text and Replace Mention Action creates an array that is based on a range of integers from 0 to the length of the mentions array. This allows us to re-purpose the objects based on an integer index. The map takes each object from the split above and replaces the integer and |||| with the name from the Mentions Select array created previously. Note that I use the integer index i.e. item() to get the correct name from the Mentions array.

FROM: range(0,length(item()?['mention']))
MAP: replace(outputs('Split_on_Mention_Compose')?[item()],concat(item(),'||||'),body('Mentions_Select')?[item()])
The mention names have now been inserted but we skip the last element

The final action Join Objects is a compose with an IF statement. If the length of the mentions array is 0 (i.e. no mentions for that comment), simply return the comment text. Else we join the above array with an empty string but we also concatenate the last element which in this case is empty. When we split the string on mentions, the last element will contain any text after the last mention and so we have to append it back on using concat.

if(equals(length(item()?['mention']),0),items('Apply_to_each')?['text'],concat(join(body('Select_Text_and_Replace_Mention'),''),last(outputs('Split_on_Mention_Compose'))))
The final comment string

Our last action is outside of the apply to each and it brings together the results of the Join Objects array. This uses the trick with the outputs() expression, which will bring all of the compose results from the apply to each, into a single array.

The result of the final compose action is an array of all of the comments, it includes the date and time the comment was posted and each of the mentions.

Pretty difficult. Is there an easier way? Please reach out and let me know if you think it can be done using a cleaner or more efficient means. How are you using this solution for your own flow?

Share