The Notes Comments API lets you create, list, edit, resolve, re-anchor, and delete comment threads attached to document nodes inside a notebook. Each comment is tied to an anchor (the whole document, a specific block, or a text range within a block). The edit, resolve, re-anchor, and delete operations support an optional expectedVersion for optimistic concurrency.
Returns lightweight thread anchor metadata for comment decorations. This endpoint is intended for clients that need to render comment markers without fetching full thread bodies.
const result = await client . notes . comments . listAnchors ( {
notebookId: " nb_8f3a1c2e " ,
curl " https://api.hoody.com/api/v1/notes/notebooks/nb_8f3a1c2e/nodes/nd_42b1e7/comment-anchors?limit=500 " \
-H " Authorization: Bearer <token> "
Name In Type Required Description notebookIdpath string Yes Identifier of the notebook containing the document nodeIdpath string Yes Identifier of the document node limitquery integer No Maximum number of anchors to return. Default: 500 offsetquery integer No Number of anchors to skip. Default: 0 cursorquery string No Opaque pagination cursor returned by a previous response
"threadId" : " thr_91c2a4 " ,
"anchorType" : " text-range " ,
"anchorBlockId" : " blk_3f2c01 " ,
"startBlockId" : " blk_3f2c01 " ,
"endBlockId" : " blk_3f2c01 " ,
"anchorQuote" : " the implementation should be idempotent " ,
"anchorContextBefore" : " we agreed that " ,
"anchorContextAfter" : " in all write paths. " ,
"anchorStatus" : " active " ,
"anchorUpdatedAt" : " 2026-01-14T09:12:33.000Z "
"anchorStatus" : " active " ,
"message" : " Invalid cursor " ,
"code" : " invalid_cursor " ,
{ "path" : " cursor " , "message" : " Cursor is malformed or expired " }
{ "path" : " nodeId " , "message" : " Node does not exist in this notebook " }
{ "path" : " nodeId " , "message" : " Node has been moved or archived " }
Returns all comments attached to a document node, paginated by cursor.
const result = await client . notes . comments . list ( {
notebookId: " nb_8f3a1c2e " ,
curl " https://api.hoody.com/api/v1/notes/notebooks/nb_8f3a1c2e/nodes/nd_42b1e7/comments?limit=100 " \
-H " Authorization: Bearer <token> "
Name In Type Required Description notebookIdpath string Yes Identifier of the notebook containing the document nodeIdpath string Yes Identifier of the document node limitquery integer No Maximum number of comments to return. Default: 100 offsetquery integer No Number of comments to skip. Default: 0 cursorquery string No Opaque pagination cursor returned by a previous response
"documentId" : " nd_42b1e7 " ,
"anchorBlockId" : " blk_3f2c01 " ,
"anchorType" : " text-range " ,
"startBlockId" : " blk_3f2c01 " ,
"endBlockId" : " blk_3f2c01 " ,
"anchorQuote" : " the implementation should be idempotent " ,
"anchorContextBefore" : " we agreed that " ,
"anchorContextAfter" : " in all write paths. " ,
"anchorStatus" : " active " ,
"anchorUpdatedAt" : " 2026-01-14T09:12:33.000Z " ,
"content" : " Should we add a note about the retry budget? " ,
"createdAt" : " 2026-01-14T09:12:33.000Z " ,
"createdBy" : " usr_a1b2c3 " ,
"createdByName" : " Jamie Park " ,
"updatedAt" : " 2026-01-14T10:04:11.000Z " ,
"message" : " Invalid cursor " ,
"code" : " invalid_cursor " ,
{ "path" : " cursor " , "message" : " Cursor is malformed or expired " }
{ "path" : " nodeId " , "message" : " Node does not exist in this notebook " }
{ "path" : " nodeId " , "message" : " Node has been moved or archived " }
Creates a new comment on a document node. The anchor field describes where the comment is attached. Pass parentId to create a reply to an existing comment.
const comment = await client . notes . comments . create ( {
notebookId: " nb_8f3a1c2e " ,
content: " Should we add a note about the retry budget? " ,
startBlockId: " blk_3f2c01 " ,
endBlockId: " blk_3f2c01 " ,
quote: " the implementation should be idempotent " ,
contextBefore: " we agreed that " ,
contextAfter: " in all write paths. " ,
curl -X POST " https://api.hoody.com/api/v1/notes/notebooks/nb_8f3a1c2e/nodes/nd_42b1e7/comments " \
-H " Authorization: Bearer <token> " \
-H " Content-Type: application/json " \
"content": "Should we add a note about the retry budget?",
"startBlockId": "blk_3f2c01",
"endBlockId": "blk_3f2c01",
"quote": "the implementation should be idempotent",
"contextBefore": "we agreed that ",
"contextAfter": " in all write paths."
Name In Type Required Description notebookIdpath string Yes Identifier of the notebook containing the document nodeIdpath string Yes Identifier of the document node
Field Type Required Description contentstring Yes Comment text. Length: 1 to 10000 characters parentIdstring No Identifier of the parent comment when this is a reply anchorBlockIdstring No Identifier of the block the comment is attached to. Use the structured anchor field for richer anchors anchorobject No Structured anchor describing where the comment is placed. One of document, block, or text-range shapes
"documentId" : " nd_42b1e7 " ,
"anchorBlockId" : " blk_3f2c01 " ,
"anchorType" : " text-range " ,
"startBlockId" : " blk_3f2c01 " ,
"endBlockId" : " blk_3f2c01 " ,
"anchorQuote" : " the implementation should be idempotent " ,
"anchorContextBefore" : " we agreed that " ,
"anchorContextAfter" : " in all write paths. " ,
"anchorStatus" : " active " ,
"anchorUpdatedAt" : " 2026-01-14T09:12:33.000Z " ,
"content" : " Should we add a note about the retry budget? " ,
"createdAt" : " 2026-01-14T09:12:33.000Z " ,
"createdBy" : " usr_a1b2c3 " ,
"createdByName" : " Jamie Park " ,
"message" : " Invalid request " ,
"code" : " invalid_request " ,
{ "path" : " content " , "message" : " content must be between 1 and 10000 characters " }
{ "path" : " nodeId " , "message" : " Node does not exist in this notebook " }
{ "path" : " anchor.startBlockId " , "message" : " Anchor block has been deleted " }
"message" : " Internal server error " ,
"code" : " internal_error " ,
Updates the root comment anchor for an existing thread. Use this when the original anchor target has moved or been edited and the comment needs to track a new location.
const updated = await client . notes . comments . reanchor ( {
notebookId: " nb_8f3a1c2e " ,
commentId: " cm_7a4f31c2 " ,
curl -X POST " https://api.hoody.com/api/v1/notes/notebooks/nb_8f3a1c2e/nodes/nd_42b1e7/comments/cm_7a4f31c2/reanchor " \
-H " Authorization: Bearer <token> " \
-H " Content-Type: application/json " \
"anchor": { "type": "block", "blockId": "blk_3f2c01" },
Name In Type Required Description notebookIdpath string Yes Identifier of the notebook containing the document nodeIdpath string Yes Identifier of the document node commentIdpath string Yes Identifier of the root comment in the thread
Field Type Required Description anchorobject Yes New anchor for the thread. One of document, block, or text-range shapes expectedVersioninteger No Current version of the comment. If it does not match, the request is rejected with 409
"documentId" : " nd_42b1e7 " ,
"anchorBlockId" : " blk_3f2c01 " ,
"anchorContextBefore" : null ,
"anchorContextAfter" : null ,
"anchorStatus" : " active " ,
"anchorUpdatedAt" : " 2026-01-14T11:02:18.000Z " ,
"content" : " Should we add a note about the retry budget? " ,
"createdAt" : " 2026-01-14T09:12:33.000Z " ,
"createdBy" : " usr_a1b2c3 " ,
"createdByName" : " Jamie Park " ,
"updatedAt" : " 2026-01-14T11:02:18.000Z " ,
"message" : " Invalid request " ,
"code" : " invalid_request " ,
{ "path" : " anchor " , "message" : " anchor must be one of document, block, or text-range " }
{ "path" : " commentId " , "message" : " Comment does not exist on this node " }
"message" : " Version conflict " ,
"code" : " version_conflict " ,
{ "path" : " expectedVersion " , "message" : " Expected version 1, found 2 " }
Marks a comment as resolved and records the resolving user and timestamp.
const resolved = await client . notes . comments . resolve ( {
notebookId: " nb_8f3a1c2e " ,
commentId: " cm_7a4f31c2 " ,
curl -X POST " https://api.hoody.com/api/v1/notes/notebooks/nb_8f3a1c2e/nodes/nd_42b1e7/comments/cm_7a4f31c2/resolve " \
-H " Authorization: Bearer <token> " \
-H " Content-Type: application/json " \
-d ' { "expectedVersion": 2 } '
Name In Type Required Description notebookIdpath string Yes Identifier of the notebook containing the document nodeIdpath string Yes Identifier of the document node commentIdpath string Yes Identifier of the comment to resolve
Field Type Required Description expectedVersioninteger No Current version of the comment. If it does not match, the request is rejected with 409
"documentId" : " nd_42b1e7 " ,
"anchorBlockId" : " blk_3f2c01 " ,
"anchorType" : " text-range " ,
"startBlockId" : " blk_3f2c01 " ,
"endBlockId" : " blk_3f2c01 " ,
"anchorQuote" : " the implementation should be idempotent " ,
"anchorContextBefore" : " we agreed that " ,
"anchorContextAfter" : " in all write paths. " ,
"anchorStatus" : " active " ,
"anchorUpdatedAt" : " 2026-01-14T09:12:33.000Z " ,
"content" : " Should we add a note about the retry budget? " ,
"createdAt" : " 2026-01-14T09:12:33.000Z " ,
"createdBy" : " usr_a1b2c3 " ,
"createdByName" : " Jamie Park " ,
"updatedAt" : " 2026-01-14T11:08:01.000Z " ,
"resolvedAt" : " 2026-01-14T11:08:01.000Z " ,
"resolvedBy" : " usr_a1b2c3 "
{ "path" : " commentId " , "message" : " Comment does not exist on this node " }
"message" : " Version conflict " ,
"code" : " version_conflict " ,
{ "path" : " expectedVersion " , "message" : " Expected version 2, found 3 " }
Edits the content of an existing comment. Only the author of the comment is permitted to edit it.
const edited = await client . notes . comments . edit ( {
notebookId: " nb_8f3a1c2e " ,
commentId: " cm_7a4f31c2 " ,
content: " Should we add a note about the retry budget and the circuit breaker? " ,
curl -X PATCH " https://api.hoody.com/api/v1/notes/notebooks/nb_8f3a1c2e/nodes/nd_42b1e7/comments/cm_7a4f31c2 " \
-H " Authorization: Bearer <token> " \
-H " Content-Type: application/json " \
"content": "Should we add a note about the retry budget and the circuit breaker?",
Name In Type Required Description notebookIdpath string Yes Identifier of the notebook containing the document nodeIdpath string Yes Identifier of the document node commentIdpath string Yes Identifier of the comment to edit
Field Type Required Description contentstring Yes New comment text. Length: 1 to 10000 characters expectedVersioninteger No Current version of the comment. If it does not match, the request is rejected with 409
"documentId" : " nd_42b1e7 " ,
"anchorBlockId" : " blk_3f2c01 " ,
"anchorType" : " text-range " ,
"startBlockId" : " blk_3f2c01 " ,
"endBlockId" : " blk_3f2c01 " ,
"anchorQuote" : " the implementation should be idempotent " ,
"anchorContextBefore" : " we agreed that " ,
"anchorContextAfter" : " in all write paths. " ,
"anchorStatus" : " active " ,
"anchorUpdatedAt" : " 2026-01-14T09:12:33.000Z " ,
"content" : " Should we add a note about the retry budget and the circuit breaker? " ,
"createdAt" : " 2026-01-14T09:12:33.000Z " ,
"createdBy" : " usr_a1b2c3 " ,
"createdByName" : " Jamie Park " ,
"updatedAt" : " 2026-01-14T10:04:11.000Z " ,
{ "path" : " commentId " , "message" : " Only the comment author can edit this comment " }
{ "path" : " commentId " , "message" : " Comment does not exist on this node " }
"message" : " Version conflict " ,
"code" : " version_conflict " ,
{ "path" : " expectedVersion " , "message" : " Expected version 1, found 2 " }
Deletes a comment and all of its replies. This action is irreversible.
const result = await client . notes . comments . delete ( {
notebookId: " nb_8f3a1c2e " ,
commentId: " cm_7a4f31c2 " ,
curl -X DELETE " https://api.hoody.com/api/v1/notes/notebooks/nb_8f3a1c2e/nodes/nd_42b1e7/comments/cm_7a4f31c2?expectedVersion=3 " \
-H " Authorization: Bearer <token> "
Name In Type Required Description notebookIdpath string Yes Identifier of the notebook containing the document nodeIdpath string Yes Identifier of the document node commentIdpath string Yes Identifier of the comment to delete expectedVersionquery integer No Current version of the comment. If it does not match, the request is rejected with 409
{ "path" : " commentId " , "message" : " Comment does not exist on this node " }
"message" : " Version conflict " ,
"code" : " version_conflict " ,
{ "path" : " expectedVersion " , "message" : " Expected version 3, found 4 " }