Skip to content

The Comments API lets you create, edit, delete, resolve, and re-anchor comment threads on document nodes within a notebook. Each comment is anchored to either the entire document, a specific block, or a text range, and tracks its resolution state for collaborative review workflows.

Returns lightweight thread anchor metadata for comment decorations. Use this endpoint to render comment decorations on the client without fetching full comment bodies.

GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comment-anchors

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comment-anchors”
NameInTypeRequiredDescription
limitqueryintegerNoMaximum number of anchors to return. Default: 500.
offsetqueryintegerNoNumber of anchors to skip. Default: 0.
cursorquerystringNoPagination cursor returned by a previous call.
notebookIdpathstringYesIdentifier of the notebook that owns the document node.
nodeIdpathstringYesIdentifier of the document node whose anchors are being listed.
{
"anchors": [
{
"threadId": "cm_7a4f31c2",
"anchor": {
"anchorType": "text-range",
"anchorBlockId": "blk_d3a1f8c2",
"startBlockId": "blk_d3a1f8c2",
"startOffset": 124,
"endBlockId": "blk_d3a1f8c2",
"endOffset": 168,
"anchorQuote": "the new pricing model is finalized",
"anchorContextBefore": "After reviewing the spec,",
"anchorContextAfter": "and we should ship it next sprint.",
"anchorStatus": "active",
"anchorUpdatedAt": "2025-04-12T18:33:07Z"
},
"anchorStatus": "active",
"resolvedAt": null,
"version": 3
},
{
"threadId": "cm_b92e01d4",
"anchor": {
"anchorType": "block",
"anchorBlockId": "blk_7c2aa1f0",
"startBlockId": null,
"startOffset": null,
"endBlockId": null,
"endOffset": null,
"anchorQuote": null,
"anchorContextBefore": null,
"anchorContextAfter": null,
"anchorStatus": "orphaned",
"anchorUpdatedAt": "2025-04-10T09:14:22Z"
},
"anchorStatus": "orphaned",
"resolvedAt": "2025-04-11T12:01:50Z",
"version": 5
}
],
"nextCursor": "eyJ0aWQiOiJjbV9iOTJlMDFkNCJ9",
"hasMore": false
}
const { anchors, hasMore, nextCursor } = await client.notes.comments.listAnchors(
"nb_8f3a1c2e",
"nd_42b1e7",
{ limit: 200 }
);

Returns all comments for a document node, including the comment body, anchor metadata, author information, and resolution state.

GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments”
NameInTypeRequiredDescription
limitqueryintegerNoMaximum number of comments to return. Default: 100.
offsetqueryintegerNoNumber of comments to skip. Default: 0.
cursorquerystringNoPagination cursor returned by a previous call.
notebookIdpathstringYesIdentifier of the notebook that owns the document node.
nodeIdpathstringYesIdentifier of the document node whose comments are being listed.
{
"comments": [
{
"id": "cm_7a4f31c2",
"documentId": "nd_42b1e7",
"parentId": null,
"anchorBlockId": "blk_d3a1f8c2",
"anchorType": "text-range",
"startBlockId": "blk_d3a1f8c2",
"startOffset": 124,
"endBlockId": "blk_d3a1f8c2",
"endOffset": 168,
"anchorQuote": "the new pricing model is finalized",
"anchorContextBefore": "After reviewing the spec,",
"anchorContextAfter": "and we should ship it next sprint.",
"anchorStatus": "active",
"anchorUpdatedAt": "2025-04-12T18:33:07Z",
"version": 3,
"content": "Should this reference the legal team before we ship?",
"createdAt": "2025-04-12T18:33:07Z",
"createdBy": "u_a1b2c3d4",
"createdByName": "Avery Chen",
"updatedAt": null,
"resolvedAt": null,
"resolvedBy": null
}
],
"nextCursor": null,
"hasMore": false
}
const { comments, hasMore } = await client.notes.comments.list(
"nb_8f3a1c2e",
"nd_42b1e7",
{ limit: 50 }
);

Creates a new comment on a document node. Provide an anchor to attach the comment to a specific block or text range; omit it to comment on the entire document.

POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments”
NameInTypeRequiredDescription
notebookIdpathstringYesIdentifier of the notebook that owns the document node.
nodeIdpathstringYesIdentifier of the document node to comment on.
FieldTypeRequiredDescription
contentstringYesComment body. Between 1 and 10000 characters.
parentIdstringNoIdentifier of a parent comment when creating a reply.
anchorBlockIdstringNoBlock identifier the comment is attached to.
anchorobjectNoAnchor descriptor (see variants below).

The anchor field is a discriminated union by type:

TypeRequired FieldsOptional Fields
documenttype
blocktype, blockId
text-rangetype, startBlockId, startOffset, endBlockId, endOffsetquote, contextBefore, contextAfter
{
"id": "cm_7a4f31c2",
"documentId": "nd_42b1e7",
"parentId": null,
"anchorBlockId": "blk_d3a1f8c2",
"anchorType": "text-range",
"startBlockId": "blk_d3a1f8c2",
"startOffset": 124,
"endBlockId": "blk_d3a1f8c2",
"endOffset": 168,
"anchorQuote": "the new pricing model is finalized",
"anchorContextBefore": "After reviewing the spec,",
"anchorContextAfter": "and we should ship it next sprint.",
"anchorStatus": "active",
"anchorUpdatedAt": "2025-04-12T18:33:07Z",
"version": 1,
"content": "Should this reference the legal team before we ship?",
"createdAt": "2025-04-12T18:33:07Z",
"createdBy": "u_a1b2c3d4",
"createdByName": "Avery Chen",
"updatedAt": null,
"resolvedAt": null,
"resolvedBy": null
}
const comment = await client.notes.comments.create("nb_8f3a1c2e", "nd_42b1e7", {
content: "Should this reference the legal team before we ship?",
anchor: {
type: "text-range",
startBlockId: "blk_d3a1f8c2",
startOffset: 124,
endBlockId: "blk_d3a1f8c2",
endOffset: 168,
quote: "the new pricing model is finalized",
contextBefore: "After reviewing the spec,",
contextAfter: "and we should ship it next sprint.",
},
});

Edits the content of an existing comment. Only the comment body can be modified; anchor information is updated through the re-anchor endpoint.

PATCH /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}

Section titled “PATCH /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}”
NameInTypeRequiredDescription
notebookIdpathstringYesIdentifier of the notebook that owns the document node.
nodeIdpathstringYesIdentifier of the document node the comment belongs to.
commentIdpathstringYesIdentifier of the comment to edit.
FieldTypeRequiredDescription
contentstringYesNew comment body. Between 1 and 10000 characters.
expectedVersionintegerNoVersion number expected on the server for optimistic concurrency control.
{
"id": "cm_7a4f31c2",
"documentId": "nd_42b1e7",
"parentId": null,
"anchorBlockId": "blk_d3a1f8c2",
"anchorType": "text-range",
"startBlockId": "blk_d3a1f8c2",
"startOffset": 124,
"endBlockId": "blk_d3a1f8c2",
"endOffset": 168,
"anchorQuote": "the new pricing model is finalized",
"anchorContextBefore": "After reviewing the spec,",
"anchorContextAfter": "and we should ship it next sprint.",
"anchorStatus": "active",
"anchorUpdatedAt": "2025-04-12T18:33:07Z",
"version": 4,
"content": "Should this reference the legal team before we ship next quarter?",
"createdAt": "2025-04-12T18:33:07Z",
"createdBy": "u_a1b2c3d4",
"createdByName": "Avery Chen",
"updatedAt": "2025-04-13T09:02:11Z",
"resolvedAt": null,
"resolvedBy": null
}
const updated = await client.notes.comments.edit(
"nb_8f3a1c2e",
"nd_42b1e7",
"cm_7a4f31c2",
{
content: "Should this reference the legal team before we ship next quarter?",
expectedVersion: 3,
}
);

Updates the root comment anchor for a comment thread. Use this when the original anchor becomes orphaned (for example, the referenced block was deleted) and you want to reattach the thread to a new location.

POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}/reanchor

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}/reanchor”
NameInTypeRequiredDescription
notebookIdpathstringYesIdentifier of the notebook that owns the document node.
nodeIdpathstringYesIdentifier of the document node the comment belongs to.
commentIdpathstringYesIdentifier of the comment thread to re-anchor.
FieldTypeRequiredDescription
anchorobjectYesNew anchor descriptor (see variants below).
expectedVersionintegerNoVersion number expected on the server for optimistic concurrency control.

The anchor field is a discriminated union by type:

TypeRequired FieldsOptional Fields
documenttype
blocktype, blockId
text-rangetype, startBlockId, startOffset, endBlockId, endOffsetquote, contextBefore, contextAfter
{
"id": "cm_7a4f31c2",
"documentId": "nd_42b1e7",
"parentId": null,
"anchorBlockId": "blk_e91a8b40",
"anchorType": "block",
"startBlockId": null,
"startOffset": null,
"endBlockId": null,
"endOffset": null,
"anchorQuote": null,
"anchorContextBefore": null,
"anchorContextAfter": null,
"anchorStatus": "active",
"anchorUpdatedAt": "2025-04-13T11:48:55Z",
"version": 5,
"content": "Should this reference the legal team before we ship next quarter?",
"createdAt": "2025-04-12T18:33:07Z",
"createdBy": "u_a1b2c3d4",
"createdByName": "Avery Chen",
"updatedAt": "2025-04-13T11:48:55Z",
"resolvedAt": null,
"resolvedBy": null
}
const reanchored = await client.notes.comments.reanchor(
"nb_8f3a1c2e",
"nd_42b1e7",
"cm_7a4f31c2",
{
anchor: { type: "block", blockId: "blk_e91a8b40" },
expectedVersion: 4,
}
);

Marks a comment as resolved. The resolvedAt and resolvedBy fields are populated on the returned comment.

POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}/resolve

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}/resolve”
NameInTypeRequiredDescription
notebookIdpathstringYesIdentifier of the notebook that owns the document node.
nodeIdpathstringYesIdentifier of the document node the comment belongs to.
commentIdpathstringYesIdentifier of the comment to resolve.
FieldTypeRequiredDescription
expectedVersionintegerNoVersion number expected on the server for optimistic concurrency control.
{
"id": "cm_7a4f31c2",
"documentId": "nd_42b1e7",
"parentId": null,
"anchorBlockId": "blk_d3a1f8c2",
"anchorType": "text-range",
"startBlockId": "blk_d3a1f8c2",
"startOffset": 124,
"endBlockId": "blk_d3a1f8c2",
"endOffset": 168,
"anchorQuote": "the new pricing model is finalized",
"anchorContextBefore": "After reviewing the spec,",
"anchorContextAfter": "and we should ship it next sprint.",
"anchorStatus": "active",
"anchorUpdatedAt": "2025-04-12T18:33:07Z",
"version": 5,
"content": "Should this reference the legal team before we ship next quarter?",
"createdAt": "2025-04-12T18:33:07Z",
"createdBy": "u_a1b2c3d4",
"createdByName": "Avery Chen",
"updatedAt": "2025-04-14T08:21:44Z",
"resolvedAt": "2025-04-14T08:21:44Z",
"resolvedBy": "u_f7e8d9c0"
}
const resolved = await client.notes.comments.resolve(
"nb_8f3a1c2e",
"nd_42b1e7",
"cm_7a4f31c2",
{ expectedVersion: 4 }
);

Deletes a comment and its replies. This operation is irreversible.

DELETE /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}

Section titled “DELETE /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}”
NameInTypeRequiredDescription
expectedVersionqueryintegerNoVersion number expected on the server for optimistic concurrency control.
notebookIdpathstringYesIdentifier of the notebook that owns the document node.
nodeIdpathstringYesIdentifier of the document node the comment belongs to.
commentIdpathstringYesIdentifier of the comment to delete.
{
"success": true
}
const result = await client.notes.comments.delete(
"nb_8f3a1c2e",
"nd_42b1e7",
"cm_7a4f31c2",
{ expectedVersion: 5 }
);