Manage notebook nodes (pages, sections, channels, databases, records), read and write document content, export documents, and track read interactions. This page covers the node lifecycle (list, create, update, delete), per-node document operations (read, replace, merge, append), SVG export of drawing blocks, secure HTML export tickets, and per-user “seen” / “opened” timestamps.
All endpoints run inside the notes service container. The base URL follows the template:
https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com
Returns a paginated list of nodes the user has access to. Filterable by type, parentId, and rootId.
Name In Type Required Description notebookIdpath string Yes The notebook identifier typequery string No Filter by node type parentIdquery string No Filter by parent node ID rootIdquery string No Filter by root node ID limitquery integer No Maximum number of nodes to return. Default: 50 offsetquery integer No Number of nodes to skip. Default: 0
" parentId " : " node_root01 " ,
" name " : " Project Onboarding " ,
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" updatedAt " : " 2026-02-03T14:05:51.000Z "
" parentId " : " node_root01 " ,
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" message " : " You do not have access to this node. " ,
{ " path " : " notebookId " , " message " : " Notebook is private " }
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . nodes . list ( ' nb_abc123 ' , { type : ' page ' , limit : 25 });
Resolves a page node by its safe alias within the notebook scope.
Name In Type Required Description notebookIdpath string Yes The notebook identifier aliaspath string Yes The safe URL-style alias of the node
" parentId " : " node_root01 " ,
" name " : " Project Onboarding " ,
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" updatedAt " : " 2026-02-03T14:05:51.000Z "
" message " : " Alias must contain only lowercase letters, digits, and dashes. " ,
{ " path " : " alias " , " message " : " Invalid alias format " }
" message " : " You do not have access to this notebook. " ,
" message " : " No node found with alias 'onboarding'. " ,
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . nodes . getByAlias ( ' nb_abc123 ' , ' onboarding ' );
Returns the full details of a single node by ID.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
" parentId " : " node_root01 " ,
" name " : " Project Onboarding " ,
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" updatedAt " : " 2026-02-03T14:05:51.000Z "
" message " : " You do not have access to this node. " ,
{ " path " : " nodeId " , " message " : " Node is private " }
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin
" message " : " Node not found. " ,
{ " path " : " nodeId " , " message " : " No node with that ID in this notebook " }
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . nodes . get ( ' nb_abc123 ' , ' node_8f3a2b1c ' );
Returns a paginated list of direct children of the specified node.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The parent node identifier limitquery integer No Maximum number of children to return. Default: 50 offsetquery integer No Number of children to skip. Default: 0
" parentId " : " node_root01 " ,
" name " : " Project Onboarding " ,
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" updatedAt " : " 2026-02-03T14:05:51.000Z "
" message " : " You do not have access to this node. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin
" message " : " Node not found. " ,
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . nodes . listChildren ( ' nb_abc123 ' , ' node_root01 ' , { limit : 10 });
Creates a new node (section, page, channel, message, database, or record) in the notebook.
Name In Type Required Description notebookIdpath string Yes The notebook identifier
Field Type Required Description idstring No Optional client-supplied node ID typestring Yes Node type (e.g. page, section, channel, database, record) parentIdstring No Identifier of the parent node attributesobject Yes Free-form attribute map (e.g. { name, alias, icon })
" parentId " : " node_root01 " ,
" parentId " : " node_root01 " ,
" createdAt " : " 2026-03-04T09:00:00.000Z " ,
" message " : " Invalid node type. " ,
{ " path " : " type " , " message " : " Unsupported node type " }
Error Code Title Description Resolution bad_requestInvalid node data The request body is invalid — missing required fields or unsupported node type Check the node type is valid and all required attributes are provided
" message " : " You do not have permission to perform this action. " ,
Error Code Title Description Resolution forbiddenInsufficient permissions User role does not have permission for this action Request a higher role from the notebook or node admin
" message " : " Idempotency key conflict. " ,
{ " path " : " id " , " message " : " A node with this ID already exists " }
Error Code Title Description Resolution bad_requestIdempotency conflict A different request was already made with the same idempotency key Use a new idempotency key for a different request
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . nodes . create ( ' nb_abc123 ' , {
Updates node attributes (name, description, etc.). type and parentId cannot be changed.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
Field Type Required Description attributesobject Yes Subset of attributes to update (e.g. { name, description, icon })
" name " : " Q2 Roadmap (final) " ,
" description " : " Cross-team roadmap for April–June " ,
" parentId " : " node_root01 " ,
" name " : " Q2 Roadmap (final) " ,
" description " : " Cross-team roadmap for April–June " ,
" createdAt " : " 2026-03-04T09:00:00.000Z " ,
" updatedAt " : " 2026-03-04T09:14:22.000Z "
" message " : " Cannot change node type. " ,
{ " path " : " type " , " message " : " Type is immutable " }
" message " : " You do not have access to this node. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin
" message " : " Node not found. " ,
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
" message " : " Node has been modified since you last read it. " ,
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . nodes . update ( ' nb_abc123 ' , ' node_8f3a2b1c ' , {
name : ' Q2 Roadmap (final) ' ,
Permanently deletes a node and its associated data (documents, files, reactions).
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
" message " : " You do not have permission to perform this action. " ,
Error Code Title Description Resolution forbiddenInsufficient permissions User role does not have permission for this action Request a higher role from the notebook or node admin
" message " : " Node not found. " ,
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . nodes . delete ( ' nb_abc123 ' , ' node_8f3a2b1c ' );
Retrieves document content for a node. Supports block filtering via blockIds and line range queries via lines.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier blockIdsquery string No Comma-separated list of block IDs to return linesquery string No Line range, e.g. 1-50 outputquery string No Output format. One of json, md, html includeCommentsquery string No none (default) or appendixticketquery string No Export ticket required for output=html
{ " type " : " text " , " text " : " Project Onboarding " }
{ " type " : " text " , " text " : " Welcome to the team. Start with the runbook below. " }
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" createdBy " : " user_9f8e7d6c " ,
" updatedAt " : " 2026-02-03T14:05:51.000Z " ,
" updatedBy " : " user_9f8e7d6c "
" message " : " Export ticket is missing, expired, or already used. " ,
" code " : " ticket_invalid " ,
{ " path " : " ticket " , " message " : " Ticket not provided " }
" message " : " You do not have access to this node. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin
" message " : " Document not found. " ,
Error Code Title Description Resolution not_foundDocument not found No document content exists for this node, or the node type does not support documents Create document content with putDocument first
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . documents . get ( ' nb_abc123 ' , ' node_8f3a2b1c ' , {
Creates a new document or fully replaces an existing document for a node.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
Field Type Required Description contentobject Yes Full document content object (ProseMirror-style: type, content, etc.)
{ " type " : " text " , " text " : " Release Notes — v3.4 " }
{ " type " : " text " , " text " : " Adds collaborative cursors and inline comments. " }
{ " type " : " text " , " text " : " Release Notes — v3.4 " }
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" createdBy " : " user_9f8e7d6c " ,
" updatedAt " : " 2026-03-04T11:02:09.000Z " ,
" updatedBy " : " user_9f8e7d6c "
" message " : " Node type does not support documents. " ,
{ " path " : " nodeId " , " message " : " Section nodes are not document-bearing " }
Error Code Title Description Resolution bad_requestUnsupported node type This node type does not support document content Only page, channel, and similar node types support documents
" message " : " You do not have permission to perform this action. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin forbiddenInsufficient permissions User role does not have permission for this action Request a higher role from the notebook or node admin
" message " : " Node not found. " ,
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . documents . put ( ' nb_abc123 ' , ' node_8f3a2b1c ' , {
content : [{ type : ' text ' , text : ' Release Notes — v3.4 ' }],
Merges content into an existing document at the top level. Existing blocks are preserved unless overwritten.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
Field Type Required Description contentobject Yes Partial document content to merge
{ " type " : " text " , " text " : " Patched at 11:02 — see changelog. " }
{ " type " : " text " , " text " : " Patched at 11:02 — see changelog. " }
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" createdBy " : " user_9f8e7d6c " ,
" updatedAt " : " 2026-03-04T11:02:09.000Z " ,
" updatedBy " : " user_9f8e7d6c "
" message " : " Node type does not support documents. " ,
Error Code Title Description Resolution bad_requestUnsupported node type This node type does not support document content Only page, channel, and similar node types support documents
" message " : " You do not have access to this node. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin forbiddenInsufficient permissions User role does not have permission for this action Request a higher role from the notebook or node admin
" message " : " Document not found. " ,
Error Code Title Description Resolution not_foundDocument not found No document exists for this node — create it with putDocument first Use putDocument to create the document before patching
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . documents . patch ( ' nb_abc123 ' , ' node_8f3a2b1c ' , {
content : [{ type : ' text ' , text : ' Patched at 11:02 — see changelog. ' }],
Appends one or more blocks to the END of a node’s rich-text document. The server assigns each block’s id, parentId, and index — any client-supplied id, parentId, index, or attrs.id is rejected. If the document does not exist yet, it is created.
Provide either text (a single block from plain text — newlines are preserved as a single literal block) or blocks (flat root blocks), never both. Allowed block types: paragraph, heading1, heading2, heading3, codeBlock, horizontalRule.
Pass X-Idempotency-Key to make retries safe. Reusing the same key with an identical body replays the original response; reusing it with a different body or node returns 409.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier X-Idempotency-Keyheader string No Optional idempotency key (max 256 chars). Reusing the same key with an identical request body and node replays the original response; reusing it with a different body or node returns 409.
Field Type Required Description textstring Conditional Plain text for a single block. Newlines are NOT split — stored as one literal block. Mutually exclusive with blocks. typestring No Block type for the text form. One of paragraph (default), heading1, heading2, heading3, codeBlock. attrsobject | null No Block-level attributes for the text form (e.g. { language: "ts" } for code blocks). blocksarray Conditional Flat root block list. Mutually exclusive with text. 1–100 items. Each block has type (default paragraph; allowed: paragraph, heading1, heading2, heading3, codeBlock, horizontalRule), content (array of inline { type: "text", text, marks }), and optional attrs.
" text " : " All hands meeting moved to Thursday at 3 PM. " ,
Or, using the blocks form:
{ " type " : " text " , " text " : " Agenda " }
{ " type " : " text " , " text " : " Roadmap, hiring, and Q&A. " }
{ " type " : " text " , " text " : " All hands meeting moved to Thursday at 3 PM. " }
" createdAt " : " 2026-01-15T10:22:18.000Z " ,
" createdBy " : " user_9f8e7d6c " ,
" updatedAt " : " 2026-03-04T11:30:00.000Z " ,
" updatedBy " : " user_9f8e7d6c " ,
" appendedBlockIds " : [ " block_91afc2 " , " block_91afc3 " ]
" message " : " This node type does not support documents. " ,
{ " path " : " nodeId " , " message " : " Section nodes are not document-bearing " }
Error Code Title Description Resolution bad_requestInvalid append request The request body is invalid — provide exactly one of text or blocks, a non-empty text, an allowed block type, no server-managed fields (id/parentId/index/attrs.id), at most 100 blocks, and content under the size limit. Also returned when the node type does not support documents. Send a valid append payload to a page (or other document-bearing) node
" message " : " You do not have permission to perform this action. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin forbiddenInsufficient permissions User role does not have permission for this action Request a higher role from the notebook or node admin
" message " : " Node not found. " ,
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
" message " : " Idempotency key already used with a different request. " ,
{ " path " : " X-Idempotency-Key " , " message " : " Key reused with a different body " }
Error Code Title Description Resolution bad_requestIdempotency key conflict The X-Idempotency-Key was already used with a different request body or node Use a fresh X-Idempotency-Key for a different append, or resend the identical request to replay
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . documents . appendDocument (
{ text : ' All hands meeting moved to Thursday at 3 PM. ' , type : ' paragraph ' },
{ XIdempotencyKey : ' append-2026-03-04-001 ' },
Creates a short-lived export ticket for static HTML document delivery. The ticket is required to fetch the document with output=html.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
Field Type Required Description outputstring No Output format. Default: "html" includeCommentsstring No none (default) or appendixincludeBackgroundboolean No Whether to include background styles. Default: true themeModestring No light or dark. Default: "dark"themeIdstring | null No Optional theme identifier (max 64 chars) themeVariablesobject No Map of theme variable overrides (string → string) fileNamestring No Suggested file name (max 128 chars)
" includeComments " : " appendix " ,
" fileName " : " Onboarding.html "
" ticket " : " tk_8f3a2b1cQ6yZ " ,
" expiresAt " : " 2026-03-04T11:45:00.000Z " ,
" message " : " You do not have permission to export this node. " ,
" message " : " Node not found. " ,
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . documents . createExportTicket ( ' nb_abc123 ' , ' node_8f3a2b1c ' , {
includeComments : ' appendix ' ,
fileName : ' Onboarding.html ' ,
Renders a drawing block as an SVG image. Supports optional background color and scale factor.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier blockIdpath string Yes The drawing block identifier bgquery string No Background color, e.g. transparent, #ffffff scalequery number No Scale factor applied to the rendered SVG
Content-Type: image/svg+xml
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 400">
<rect width="600" height="400" fill="#ffffff"/>
<path d="M 50 200 Q 300 50 550 200" stroke="#111" stroke-width="3" fill="none"/>
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . documents . exportBlockSvg ( ' nb_abc123 ' , ' node_8f3a2b1c ' , ' block_d12 ' , {
Records that the current user has opened the node. Tracks first and last opened timestamps.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
Field Type Required Description openedAtstring No ISO-8601 timestamp; defaults to server time if omitted
" openedAt " : " 2026-03-04T11:02:09.000Z "
" nodeId " : " node_8f3a2b1c " ,
" collaboratorId " : " user_9f8e7d6c " ,
" firstSeenAt " : " 2026-02-01T08:11:00.000Z " ,
" lastSeenAt " : " 2026-03-04T11:02:09.000Z " ,
" firstOpenedAt " : " 2026-03-04T11:02:09.000Z " ,
" lastOpenedAt " : " 2026-03-04T11:02:09.000Z "
" message " : " You do not have access to this node. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin
" message " : " Node not found. " ,
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
" message " : " Idempotency key conflict. " ,
Error Code Title Description Resolution bad_requestIdempotency conflict A different request was already made with the same idempotency key Use a new idempotency key for a different request
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . interactions . markOpened ( ' nb_abc123 ' , ' node_8f3a2b1c ' , {
openedAt : ' 2026-03-04T11:02:09.000Z ' ,
Records that the current user has seen the node. Tracks first and last seen timestamps.
Name In Type Required Description notebookIdpath string Yes The notebook identifier nodeIdpath string Yes The node identifier
Field Type Required Description seenAtstring No ISO-8601 timestamp; defaults to server time if omitted
" seenAt " : " 2026-03-04T11:01:55.000Z "
" nodeId " : " node_8f3a2b1c " ,
" collaboratorId " : " user_9f8e7d6c " ,
" firstSeenAt " : " 2026-02-01T08:11:00.000Z " ,
" lastSeenAt " : " 2026-03-04T11:01:55.000Z " ,
" firstOpenedAt " : " 2026-03-04T11:02:09.000Z " ,
" lastOpenedAt " : " 2026-03-04T11:02:09.000Z "
" message " : " You do not have access to this node. " ,
Error Code Title Description Resolution forbiddenAccess denied User does not have permission to access this node Check collaborator list or request access from the node admin
" message " : " Node not found. " ,
Error Code Title Description Resolution not_foundNode not found No node exists with the provided ID in this notebook Verify node ID using listNodes or listNodeChildren
" message " : " Idempotency key conflict. " ,
Error Code Title Description Resolution bad_requestIdempotency conflict A different request was already made with the same idempotency key Use a new idempotency key for a different request
" message " : " An unexpected error occurred. " ,
Error Code Title Description Resolution unknownInternal server error An unexpected error occurred while processing the request Retry the request; if it persists, contact support
import { HoodyClient } from ' hoody-sdk ' ;
const client = new HoodyClient ({ baseURL : ' https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com ' , token : process . env . HOODY_TOKEN });
await client . notes . interactions . markSeen ( ' nb_abc123 ' , ' node_8f3a2b1c ' , {
seenAt : ' 2026-03-04T11:01:55.000Z ' ,