Work with comments and suggestions

Google Docs lets collaborators collaborate by writing comments and making suggestions which act as deferred edits waiting for approval.

You can use the API to view suggested changes inline within the document text. In Developer Preview, you can also programmatically read, create, reply to, update, or delete comment and suggestion threads.

When you use the documents.get method to fetch document content, the content might include unresolved suggestions. To control how documents.get represents suggestions, use the optional SuggestionsViewMode parameter. The following filter conditions are available with this parameter:

  • Get content with SUGGESTIONS_INLINE, so text pending either deletion or insertion appears in the document.
  • Get content as a preview with all suggestions accepted.
  • Get content as a preview, without suggestions, with all suggestions rejected.

If you don't provide SuggestionsViewMode, the Google Docs API uses a default setting appropriate to the current user’s privileges.

Suggestions & indexes

One reason the SuggestionsViewMode is important is that the indexes in the response might vary depending on whether there are suggestions, as shown below.

Content with suggestions Content without suggestions
{
 "tabs": [
  {
   "documentTab": {
    "body": {
     "content": [
      {
       "startIndex": 1,
       "endIndex": 31,
       "paragraph": {
        "elements": [
         {
          "startIndex": 1,
          "endIndex": 31,
          "textRun": {
           "content": "Text preceding the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 31,
       "endIndex": 51,
       "paragraph": {
        "elements": [
         {
          "startIndex": 31,
          "endIndex": 50,
          "textRun": {
           "content": "Suggested insertion",
           "suggestedInsertionIds": [
            "suggest.vcti8ewm4mww"
           ],
           "textStyle": {}
          }
         },
         {
          "startIndex": 50,
          "endIndex": 51,
          "textRun": {
           "content": "\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 51,
       "endIndex": 81,
       "paragraph": {
        "elements": [
         {
          "startIndex": 51,
          "endIndex": 81,
          "textRun": {
           "content": "Text following the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      }
     ]
    }
   }
  }
 ]
},

{
 "tabs": [
  {
   "documentTab": {
    "body": {
     "content": [
      {
       "startIndex": 1,
       "endIndex": 31,
       "paragraph": {
        "elements": [
         {
          "startIndex": 1,
          "endIndex": 31,
          "textRun": {
           "content": "Text preceding the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 31,
       "endIndex": 32,
       "paragraph": {
        "elements": [
         {
          "startIndex": 31,
          "endIndex": 32,
          "textRun": {
           "content": "\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      },
      {
       "startIndex": 32,
       "endIndex": 62,
       "paragraph": {
        "elements": [
         {
          "startIndex": 32,
          "endIndex": 62,
          "textRun": {
           "content": "Text following the suggestion\n",
           "textStyle": {}
          }
         }
        ],
        "paragraphStyle": {
         "namedStyleType": "NORMAL_TEXT",
         "direction": "LEFT_TO_RIGHT"
        }
       }
      }
     ]
    }
   }
  }
 ]
},

In the response above, the paragraph containing the line "Text following the suggestion" shows the difference when using SuggestionsViewMode. With the value set to SUGGESTIONS_INLINE, the startIndex of the ParagraphElement begins at 51 and the endIndex stops at 81. Without suggestions, the startIndex and endIndex range from 32–62.

Get content without suggestions

The following partial code sample shows how to get a document as a preview with all suggestions rejected (if there are any) by setting the SuggestionsViewMode parameter to PREVIEW_WITHOUT_SUGGESTIONS.

Java

final string SUGGEST_MODE = "PREVIEW_WITHOUT_SUGGESTIONS";
Document doc =
    service
        .documents()
        .get(DOCUMENT_ID)
        .setIncludeTabsContent(true)
        .setSuggestionsViewMode(SUGGEST_MODE)
        .execute();

Python

SUGGEST_MODE = "PREVIEW_WITHOUT_SUGGESTIONS"
result = (
  service.documents()
  .get(
      documentId=DOCUMENT_ID,
      includeTabsContent=True,
      suggestionsViewMode=SUGGEST_MODE,
  )
  .execute()
)

Omitting the SuggestionsViewMode parameter is equivalent to providing DEFAULT_FOR_CURRENT_ACCESS as the parameter value.

Style suggestions

Documents can also have style suggestions. These are suggested changes to formatting and presentation, rather than changes to content.

Unlike text insertions or deletions, these don't offset the indexes—although they might break up a TextRun into smaller chunks—but just add annotations about the suggested style change.

One such annotation is a SuggestedTextStyle, which consists of 2 parts:

  • The textStyle, which describes how the text is styled after the suggested change, but doesn't say what changed.

  • The textStyleSuggestionState, which indicates how the suggestion alters the fields of the textStyle.

You can see this in the following document tab extract, which includes a suggested style change:

[01] "paragraph": {
[02]    "elements": [
[03]        {
[04]            "endIndex": 106,
[05]            "startIndex": 82,
[06]            "textRun": {
[07]                "content": "Some text that does not ",
[08]                "textStyle": {}
[09]            }
[10]        },
[11]        {
[12]            "endIndex": 115,
[13]            "startIndex": 106,
[14]            "textRun": {
[15]                "content": "initially",
[16]                "suggestedTextStyleChanges": {
[17]                    "suggest.xymysbs9zldp": {
[18]                        "textStyle": {
[19]                            "backgroundColor": {},
[20]                            "baselineOffset": "NONE",
[21]                            "bold": true,
[22]                            "fontSize": {
[23]                                "magnitude": 11,
[24]                                "unit": "PT"
[25]                            },
[26]                            "foregroundColor": {
[27]                                "color": {
[28]                                    "rgbColor": {}
[29]                                }
[30]                            },
[31]                            "italic": false,
[32]                            "smallCaps": false,
[33]                            "strikethrough": false,
[34]                            "underline": false
[35]                        },
[36]                        "textStyleSuggestionState": {
[37]                            "boldSuggested": true,
[38]                            "weightedFontFamilySuggested": true
[39]                        }
[40]                    }
[41]                },
[42]                "textStyle": {
[43]                    "italic": true
[44]                }
[45]            }
[46]        },
[47]        {
[48]            "endIndex": 143,
[49]            "startIndex": 115,
[50]            "textRun": {
[51]                "content": " contain any boldface text.\n",
[52]                "textStyle": {}
[53]            }
[54]        }
[55]    ],
[56]    "paragraphStyle": {
[57]        "direction": "LEFT_TO_RIGHT",
[58]        "namedStyleType": "NORMAL_TEXT"
[59]    }
[60] }

In the sample above, the paragraph consists of 3 text runs, starting at lines 6, 14, and 50. Examine the middle text run:

  • Line 16: There's a suggestedTextStyleChanges object.
  • Line 18: The textStyle specifies various formatting.
  • Line 36: The textStyleSuggestionState tells you that only the bold part of this specification was the suggestion.
  • Line 42: The italic styling of this text run is part of the current document (and not affected by the suggestion).

Only the style features set to true in the textStyleSuggestionState are part of the suggestion.

Create and manage comments

You can programmatically add comments and replies, edit comments, and delete comments or replies using the documents.batchUpdate method.

When performing batch updates involving comments or suggestions, you should monitor for potential partial failures. For more information, see Comment and suggestion update status.

Insert a comment

To insert a comment thread, use the InsertCommentRequest object. You must provide the comment text contents, and an anchor location (such as a range) where the comment is attached.

The following JSON example adds a non-assigned comment thread to the specified range:

{
  "requests": [
    {
      "insertComment": {
        "content": "This is a comment added via the API.",
        "range": {
          "startIndex": 10,
          "endIndex": 25
        }
      }
    }
  ]
}

You can assign a comment to a specific user by providing their email in the assigneeEmailAddress field:

{
  "requests": [
    {
      "insertComment": {
        "content": "Please review this paragraph.",
        "assigneeEmailAddress": "user@example.com",
        "range": {
          "startIndex": 10,
          "endIndex": 25
        }
      }
    }
  ]
}

Add a reply or take action

To reply to a comment or suggestion thread, or to resolve or reopen a thread, use AddCommentReplyRequest.

A reply is represented by a Post object. The Post object contains the reply content and can optionally specify a commentAction (to RESOLVE or REOPEN the thread).

The following sample replies to an existing comment thread:

{
  "requests": [
    {
      "addCommentReply": {
        "commentId": "comment_thread_id",
        "post": {
          "content": "Replying to the comment thread."
        }
      }
    }
  ]
}

The following sample resolves a comment thread, which does not require content:

{
  "requests": [
    {
      "addCommentReply": {
        "commentId": "comment_thread_id",
        "post": {
          "commentAction": "RESOLVE"
        }
      }
    }
  ]
}

Edit a post

To edit the text content of a post you authored, use UpdateCommentPostRequest. You must specify the thread ID (either commentId or suggestionId), the postId of the post you want to edit, and the new plain text content.

Note that you cannot edit the head post of a suggestion thread (as those are generated by suggest-mode edits).

{
  "requests": [
    {
      "updateCommentPost": {
        "commentId": "comment_thread_id",
        "postId": "post_id",
        "content": "This is the updated comment text."
      }
    }
  ]
}

Delete comments and replies

  • Delete a comment thread: To remove an entire comment thread, use DeleteCommentRequest. You can only delete a comment thread if you're the author of the thread's head post.
  • Delete a reply: To delete a specific reply post, use DeleteCommentReplyRequest. You can only delete replies you authored. You cannot delete reply posts that contain actions or assignees.

The following sample deletes a comment thread:

{
  "requests": [
    {
      "deleteComment": {
        "commentId": "comment_thread_id"
      }
    }
  ]
}

Write suggestions and manage suggestion threads

You can write edits as suggestions rather than direct edits, and programmatically accept, reject, or delete suggestion threads.

When performing batch updates involving suggestions, you should monitor for potential partial failures. For more information, see Comment and suggestion update status.

Create suggestions using suggest mode

To apply edits as suggestions, set the writeMode field of the WriteControl object to SUGGEST in your batch update request. All updates in the request are processed as suggestions.

{
  "requests": [
    {
      "insertText": {
        "text": "suggested insertion text",
        "location": {
          "index": 1
        }
      }
    }
  ],
  "writeControl": {
    "writeMode": "SUGGEST"
  }
}

Unsupported requests in suggest mode

When using WriteMode.SUGGEST, the following request types aren't supported and will return an error:

  • AddDocumentTab
  • CreateNamedRange
  • DeleteFooter
  • DeleteHeader
  • DeleteNamedRange
  • DeleteTab
  • UpdateDocumentTabProperties
  • UpdateTableColumnProperties

Additionally, you cannot suggest changes to document format or headers/footers settings. In UpdateDocumentStyle, suggestions aren't supported for the following style types:

  • documentFormat
  • useEvenPageHeaderFooter
  • useFirstPageHeaderFooter

Accept, reject, or delete suggestion threads

You can manage suggestion threads using the following requests:

  • Accept suggestion: Use AcceptSuggestionRequest to accept the suggestion. This requires edit access to the document.
  • Reject suggestion: Use RejectSuggestionRequest to reject the suggestion. This requires edit access to the document or being the author of the suggestion.
  • Delete suggestion: Use DeleteSuggestionRequest to delete the suggestion. This requires being the author of the suggestion.

The following sample accepts a suggestion thread:

{
  "requests": [
    {
      "acceptSuggestion": {
        "suggestionId": "suggestion_thread_id"
      }
    }
  ]
}

Comment and suggestion update status

Requests that require saving comment or suggestion threads (such as inserting comments, adding replies, or making suggestions) might experience partial failures. In these cases, the document model changes (such as text insertions or deletions) might be successfully committed to the Docs model, but the associated comments or suggestions might fail to save.

You can verify whether comment or suggestion updates were successfully applied by checking the commentUpdateState field in the BatchUpdateDocumentResponse.

The following states are returned in CommentUpdateState:

  • NO_UPDATES_REQUESTED: No comment or suggestion updates were requested in the batch operation.
  • ALL_SAVED: All requested comment or suggestion updates were successfully applied.
  • ALL_FAILED_UNKNOWN_REASON: All requested comment or suggestion updates failed to save, even though the Docs model changes might have been committed.