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 thetextStyle.
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
suggestedTextStyleChangesobject. - Line 18: The
textStylespecifies various formatting. - Line 36: The
textStyleSuggestionStatetells 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:
AddDocumentTabCreateNamedRangeDeleteFooterDeleteHeaderDeleteNamedRangeDeleteTabUpdateDocumentTabPropertiesUpdateTableColumnProperties
Additionally, you cannot suggest changes to document format or headers/footers settings. In UpdateDocumentStyle, suggestions aren't supported for the following style types:
documentFormatuseEvenPageHeaderFooteruseFirstPageHeaderFooter
Accept, reject, or delete suggestion threads
You can manage suggestion threads using the following requests:
- Accept suggestion: Use
AcceptSuggestionRequestto accept the suggestion. This requires edit access to the document. - Reject suggestion: Use
RejectSuggestionRequestto reject the suggestion. This requires edit access to the document or being the author of the suggestion. - Delete suggestion: Use
DeleteSuggestionRequestto 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.