Skip to content
Hoody.com

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

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

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes”

Returns a paginated list of nodes the user has access to. Filterable by type, parentId, and rootId.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
typequerystringNoFilter by node type
parentIdquerystringNoFilter by parent node ID
rootIdquerystringNoFilter by root node ID
limitqueryintegerNoMaximum number of nodes to return. Default: 50
offsetqueryintegerNoNumber of nodes to skip. Default: 0
{
"nodes": [
{
"id": "node_8f3a2b1c",
"type": "page",
"parentId": "node_root01",
"attributes": {
"name": "Project Onboarding",
"alias": "onboarding",
"icon": "rocket"
},
"createdAt": "2026-01-15T10:22:18.000Z",
"updatedAt": "2026-02-03T14:05:51.000Z"
},
{
"id": "node_8f3a2b1d",
"type": "section",
"parentId": "node_root01",
"attributes": {
"name": "Engineering"
},
"createdAt": "2026-01-15T10:22:18.000Z",
"updatedAt": null
}
],
"total": 142
}
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 });

GET /api/v1/notes/notebooks/{notebookId}/nodes/alias/{alias}

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes/alias/{alias}”

Resolves a page node by its safe alias within the notebook scope.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
aliaspathstringYesThe safe URL-style alias of the node
{
"id": "node_8f3a2b1c",
"type": "page",
"parentId": "node_root01",
"attributes": {
"name": "Project Onboarding",
"alias": "onboarding",
"icon": "rocket"
},
"createdAt": "2026-01-15T10:22:18.000Z",
"updatedAt": "2026-02-03T14:05:51.000Z"
}
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');

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

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}”

Returns the full details of a single node by ID.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
{
"id": "node_8f3a2b1c",
"type": "page",
"parentId": "node_root01",
"attributes": {
"name": "Project Onboarding",
"alias": "onboarding",
"icon": "rocket"
},
"createdAt": "2026-01-15T10:22:18.000Z",
"updatedAt": "2026-02-03T14:05:51.000Z"
}
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');

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

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/children”

Returns a paginated list of direct children of the specified node.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe parent node identifier
limitqueryintegerNoMaximum number of children to return. Default: 50
offsetqueryintegerNoNumber of children to skip. Default: 0
{
"nodes": [
{
"id": "node_8f3a2b1c",
"type": "page",
"parentId": "node_root01",
"attributes": {
"name": "Project Onboarding",
"alias": "onboarding"
},
"createdAt": "2026-01-15T10:22:18.000Z",
"updatedAt": "2026-02-03T14:05:51.000Z"
}
],
"total": 7
}
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 });

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

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes”

Creates a new node (section, page, channel, message, database, or record) in the notebook.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
FieldTypeRequiredDescription
idstringNoOptional client-supplied node ID
typestringYesNode type (e.g. page, section, channel, database, record)
parentIdstringNoIdentifier of the parent node
attributesobjectYesFree-form attribute map (e.g. { name, alias, icon })
{
"type": "page",
"parentId": "node_root01",
"attributes": {
"name": "Q2 Roadmap",
"alias": "q2-roadmap",
"icon": "star"
}
}
{
"id": "node_d34db33f",
"type": "page",
"parentId": "node_root01",
"attributes": {
"name": "Q2 Roadmap",
"alias": "q2-roadmap",
"icon": "star"
},
"createdAt": "2026-03-04T09:00:00.000Z",
"updatedAt": null
}
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', {
type: 'page',
parentId: 'node_root01',
attributes: {
name: 'Q2 Roadmap',
alias: 'q2-roadmap',
icon: 'star',
},
});

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

Section titled “PATCH /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}”

Updates node attributes (name, description, etc.). type and parentId cannot be changed.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
FieldTypeRequiredDescription
attributesobjectYesSubset of attributes to update (e.g. { name, description, icon })
{
"attributes": {
"name": "Q2 Roadmap (final)",
"description": "Cross-team roadmap for April–June",
"icon": "calendar"
}
}
{
"id": "node_d34db33f",
"type": "page",
"parentId": "node_root01",
"attributes": {
"name": "Q2 Roadmap (final)",
"description": "Cross-team roadmap for April–June",
"icon": "calendar"
},
"createdAt": "2026-03-04T09:00:00.000Z",
"updatedAt": "2026-03-04T09:14:22.000Z"
}
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', {
attributes: {
name: 'Q2 Roadmap (final)',
icon: 'calendar',
},
});

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

Section titled “DELETE /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}”

Permanently deletes a node and its associated data (documents, files, reactions).

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
{
"success": true
}
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');

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

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document”

Retrieves document content for a node. Supports block filtering via blockIds and line range queries via lines.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
blockIdsquerystringNoComma-separated list of block IDs to return
linesquerystringNoLine range, e.g. 1-50
outputquerystringNoOutput format. One of json, md, html
includeCommentsquerystringNonone (default) or appendix
ticketquerystringNoExport ticket required for output=html
{
"id": "doc_a1b2c3d4",
"content": {
"type": "doc",
"content": [
{
"type": "heading1",
"content": [
{ "type": "text", "text": "Project Onboarding" }
]
},
{
"type": "paragraph",
"content": [
{ "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"
}
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', {
output: 'md',
includeComments: 'none',
});

PUT /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document

Section titled “PUT /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document”

Creates a new document or fully replaces an existing document for a node.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
FieldTypeRequiredDescription
contentobjectYesFull document content object (ProseMirror-style: type, content, etc.)
{
"content": {
"type": "doc",
"content": [
{
"type": "heading1",
"content": [
{ "type": "text", "text": "Release Notes — v3.4" }
]
},
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Adds collaborative cursors and inline comments." }
]
}
]
}
}
{
"id": "doc_a1b2c3d4",
"content": {
"type": "doc",
"content": [
{
"type": "heading1",
"content": [
{ "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"
}
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: 'doc',
content: [
{
type: 'heading1',
content: [{ type: 'text', text: 'Release Notes — v3.4' }],
},
],
},
});

PATCH /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document

Section titled “PATCH /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document”

Merges content into an existing document at the top level. Existing blocks are preserved unless overwritten.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
FieldTypeRequiredDescription
contentobjectYesPartial document content to merge
{
"content": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Patched at 11:02 — see changelog." }
]
}
]
}
}
{
"id": "doc_a1b2c3d4",
"content": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "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"
}
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: 'doc',
content: [
{
type: 'paragraph',
content: [{ type: 'text', text: 'Patched at 11:02 — see changelog.' }],
},
],
},
});

POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document/append

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document/append”

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.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
X-Idempotency-KeyheaderstringNoOptional 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.
FieldTypeRequiredDescription
textstringConditionalPlain text for a single block. Newlines are NOT split — stored as one literal block. Mutually exclusive with blocks.
typestringNoBlock type for the text form. One of paragraph (default), heading1, heading2, heading3, codeBlock.
attrsobject | nullNoBlock-level attributes for the text form (e.g. { language: "ts" } for code blocks).
blocksarrayConditionalFlat 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.",
"type": "paragraph"
}

Or, using the blocks form:

{
"blocks": [
{
"type": "heading2",
"content": [
{ "type": "text", "text": "Agenda" }
]
},
{
"type": "paragraph",
"content": [
{ "type": "text", "text": "Roadmap, hiring, and Q&A." }
]
},
{
"type": "horizontalRule"
}
]
}
{
"id": "doc_a1b2c3d4",
"content": {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{ "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"]
}
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(
'nb_abc123',
'node_8f3a2b1c',
{ text: 'All hands meeting moved to Thursday at 3 PM.', type: 'paragraph' },
{ XIdempotencyKey: 'append-2026-03-04-001' },
);

POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/export-ticket

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/export-ticket”

Creates a short-lived export ticket for static HTML document delivery. The ticket is required to fetch the document with output=html.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
FieldTypeRequiredDescription
outputstringNoOutput format. Default: "html"
includeCommentsstringNonone (default) or appendix
includeBackgroundbooleanNoWhether to include background styles. Default: true
themeModestringNolight or dark. Default: "dark"
themeIdstring | nullNoOptional theme identifier (max 64 chars)
themeVariablesobjectNoMap of theme variable overrides (string → string)
fileNamestringNoSuggested file name (max 128 chars)
{
"output": "html",
"includeComments": "appendix",
"themeMode": "light",
"fileName": "Onboarding.html"
}
{
"ticket": "tk_8f3a2b1cQ6yZ",
"expiresAt": "2026-03-04T11:45:00.000Z",
"usesRemaining": 3
}
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', {
output: 'html',
includeComments: 'appendix',
themeMode: 'light',
fileName: 'Onboarding.html',
});

GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/blocks/{blockId}/svg

Section titled “GET /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/blocks/{blockId}/svg”

Renders a drawing block as an SVG image. Supports optional background color and scale factor.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
blockIdpathstringYesThe drawing block identifier
bgquerystringNoBackground color, e.g. transparent, #ffffff
scalequerynumberNoScale 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"/>
</svg>
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', {
bg: 'transparent',
scale: 2,
});

POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/interactions/opened

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/interactions/opened”

Records that the current user has opened the node. Tracks first and last opened timestamps.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
FieldTypeRequiredDescription
openedAtstringNoISO-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"
}
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',
});

POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/interactions/seen

Section titled “POST /api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/interactions/seen”

Records that the current user has seen the node. Tracks first and last seen timestamps.

NameInTypeRequiredDescription
notebookIdpathstringYesThe notebook identifier
nodeIdpathstringYesThe node identifier
FieldTypeRequiredDescription
seenAtstringNoISO-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"
}
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',
});