Below is a copy/paste, click-by-click build recipe for a Power Automate flow that detects new modern SharePoint list item comments and sends notifications without duplicates.

This design is based on the fact that modern Comments are not a normal column change, so we use “Send an HTTP request to SharePoint” (REST) when the standard connector can’t do it. (Microsoft Learn)
(And yes—comments can be disabled by admins, which affects the UI and your automation expectations. (Microsoft Support))


Workflow A (Recommended): Scheduled polling + REST /comments + state list

0) Create the helper list (state store)

Create a new SharePoint list (same site is easiest) named:

Comment Notification State

Columns (besides default Title):

  • ListTitle (Single line of text)
  • ItemId (Number)
  • LastCommentId (Number)
  • LastCommentTime (Date and time)
  • LastRun (Date and time)
  • NotifiedTo (Single line of text) (optional)

Title will store a unique key like:
Example List|123

This is what prevents duplicate notifications.


1) Create the flow

Trigger

Recurrence

  • Interval: 5
  • Frequency: Minute

2) Get the target items (from your business list)

Get items (SharePoint)

  • Site Address: https://contoso.sharepoint.com/sites/ExampleSite
  • List Name: Example List
  • (Optional but recommended) Filter Query example:
    • Status eq 'Active'
  • (Optional) Top Count: 200 (or whatever makes sense)

This keeps the scan small and fast.


3) For each item → retrieve comments via REST

Add: Apply to each
Input: value from Get items

Inside the loop:

3.1) HTTP GET comments

Add action: Send an HTTP request to SharePoint (Microsoft Learn)

  • Site Address: https://contoso.sharepoint.com/sites/ExampleSite
  • Method: GET
  • Uri:_api/web/lists/getbytitle('Example List')/items(@{items('Apply_to_each')?['ID']})/comments

This “comments live under Items()” pattern is the key. (Stack Overflow)

  • Headers:
    • Key: Accept
    • Value: application/json;odata=nometadata

4) Parse comments response

Add Parse JSON

  • Content:body('Send_an_HTTP_request_to_SharePoint')

Schema (safe minimal)

Use this schema (works for common responses; if your tenant differs, generate schema from a sample run):

{
"type": "object",
"properties": {
"value": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"text": { "type": "string" },
"createdDate": { "type": "string" },
"author": {
"type": "object",
"properties": {
"email": { "type": "string" },
"name": { "type": "string" }
}
}
},
"required": [ "id" ]
}
}
}
}

5) Short-circuit: if there are no comments, skip

Add Condition:

Left expression:

length(body('Parse_JSON')?['value'])

Operator: is equal to
Right: 0

If yes (0): do nothing (or just update LastRun if you want).
If no: continue.


6) Compute “latest comment” reliably

6.1) Select comment IDs

Add Select

  • From:body('Parse_JSON')?['value']
  • Map:
    • Key: id
    • Value:item()?['id']

6.2) Latest comment ID

Add Compose → name it LatestCommentId

Expression:

max(body('Select'))

This avoids assuming the API returns comments sorted.

6.3) Get the latest comment object

Add Filter array

  • From:body('Parse_JSON')?['value']
  • Condition:
    • Left:item()?['id']
    • Operator: is equal to
    • Right:outputs('LatestCommentId')

Add Compose → name it LatestComment
Expression:

first(body('Filter_array'))

Now you can reference:

  • Latest text: outputs('LatestComment')?['text']
  • Latest time: outputs('LatestComment')?['createdDate']
  • Latest author name/email: outputs('LatestComment')?['author']?['name'], ...['email']

7) Read state for this item (prevent duplicates)

Add Get items (from state list)

  • Site Address: same site
  • List Name: Comment Notification State
  • Filter Query:Title eq 'Example List|@{items('Apply_to_each')?['ID']}'
  • Top Count: 1

Add Condition: “State exists?”
Left:

length(body('Get_items_-_State')?['value'])

Operator: is equal to
Right: 0

7.A) If no state exists (first time we see this item)

Create item (in state list)

  • Title:Example List|@{items('Apply_to_each')?['ID']}
  • ListTitle: Example List
  • ItemId: @{items('Apply_to_each')?['ID']}
  • LastCommentId: @{outputs('LatestCommentId')}
  • LastCommentTime: @{outputs('LatestComment')?['createdDate']}
  • LastRun: @{utcNow()}

✅ And do not send a notification on first discovery (recommended), otherwise you’ll spam for old comments.


7.B) If state exists, compare and notify only if new

Add Compose → name it StateItem
Expression:

first(body('Get_items_-_State')?['value'])

Add Condition: “Is there a new comment?”
Left:

outputs('LatestCommentId')

Operator: is greater than
Right:

outputs('StateItem')?['LastCommentId']

If NO (not greater): do nothing (optional update LastRun only).

If YES: send notification + update state


8) Send the notification

8.1) Build item link

If your Get items output already provides Link to item, use it.

If not, create a link like:

Add Compose → name it ItemLink

concat(
'https://contoso.sharepoint.com/sites/ExampleSite',
'/Lists/Example%20List/DispForm.aspx?ID=',
string(items('Apply_to_each')?['ID'])
)

8.2) Recipients

Common options:

  • Created By Email (from item)
  • Assigned To email (Person field)
  • Watchers field (multi-person)
  • A fixed mailbox or Teams channel

Example (Created By):
Use the dynamic content Created By Email (often Author Email) from SharePoint Get items output.

8.3) Send email

Action: Send an email (V2)

Subject example:

New comment on: @{items('Apply_to_each')?['Title']}

Body example (HTML is fine):

<p><b>List:</b> Example List</p>
<p><b>Item:</b> @{items('Apply_to_each')?['Title']}</p>
<p><b>Comment by:</b> @{outputs('LatestComment')?['author']?['name']} (@{outputs('LatestComment')?['author']?['email']})</p>
<p><b>When:</b> @{outputs('LatestComment')?['createdDate']}</p>
<p><b>Comment:</b><br/>@{outputs('LatestComment')?['text']}</p>
<p><a href="@{outputs('ItemLink')}">Open item</a></p>

Note: SharePoint already supports @mentions in comments and can notify mentioned users. (Microsoft Support)
Your flow is typically for non-mention routing (e.g., notify assignee/owner/watchers).


9) Update the state record (critical)

Action: Update item (in state list)

  • ID (of the state item):outputs('StateItem')?['ID']
  • LastCommentId:outputs('LatestCommentId')
  • LastCommentTime:outputs('LatestComment')?['createdDate']
  • LastRun:utcNow()
  • NotifiedTo (optional):
    • set to the recipient(s) you used

Workflow hardening tips (do these)

A) Avoid false positives / old comments

The “first time state creation = no notify” rule prevents initial spam.

B) Throttling / performance

  • Keep your Get items filtered (Active items only).
  • Increase the recurrence to 10–15 minutes if the list is large.

C) Why we don’t use “Get changes for an item”

Because it detects property/column changes, not comment-thread changes. (Microsoft Learn)

D) REST fundamentals

Your REST approach follows Microsoft’s documented SharePoint REST patterns for list/item access. (Microsoft Learn)


Final summary tables

Build steps

StepActionPurpose
1RecurrencePoll on schedule
2Get items (business list)Choose which items to check
3Send HTTP request (GET /comments)Retrieve modern comments thread (Microsoft Learn)
4Parse JSONWork with comments array
5Select + max()Identify latest comment id
6Get items (state list)Load last processed comment
7Compare idsDetect only new comments
8Send email/TeamsNotify
9Update statePrevent duplicates

Key technical choices

ChoiceWhat we usedWhy
Comment accessREST /items(ID)/commentsComments aren’t standard fields (Stack Overflow)
Connector gap“Send an HTTP request to SharePoint”Microsoft’s recommended escape hatch (Microsoft Learn)
Duplicate preventionState list with LastCommentIdReliable, auditable
Change detectionCompare numeric comment IDsSimple and robust

Edvaldo Guimrães Filho Avatar

Published by