Notes: Real-time & Sync
Section titled “Notes: Real-time & Sync”The Notes real-time APIs drive live collaboration inside a Hoody notebook container. Use these endpoints to identify the current user, open a WebSocket channel for receiving live events, and synchronize batches of client-side mutations back to the server.
All operations on this page run against the Notes container for your project:
https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com
Identity
Section titled “Identity”Get current identity
Section titled “Get current identity”GET /api/v1/notes/me
Returns the current user identity, including userId, username, role, and notebookId. The user and notebook are auto-provisioned on first call.
This endpoint takes no parameters.
curl -X GET "https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com/api/v1/notes/me" \ -H "Authorization: Bearer <token>"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.identity.get();{ "userId": "67e89abc123def456789abcd", "username": "ada.lovelace", "role": "editor", "notebookId": "890abcdef12345678901cdef"}WebSocket lifecycle
Section titled “WebSocket lifecycle”A live collaboration channel requires two steps: initialize a socket session to obtain a socket ID, then open the WebSocket using that ID.
Initialize a WebSocket session
Section titled “Initialize a WebSocket session”POST /api/v1/notes/sockets
Creates a new socket session and returns the socket ID needed to open the WebSocket.
This endpoint takes no parameters.
curl -X POST "https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com/api/v1/notes/sockets" \ -H "Authorization: Bearer <token>"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.sockets.init();{ "id": "9c3f1e7a4b2d8f6e5a0c1d2e"}{ "message": "Invalid request", "code": "BAD_REQUEST", "details": [ { "path": "/", "message": "Request body must be empty" } ]}{ "message": "Internal server error", "code": "INTERNAL_SERVER_ERROR"}Open a WebSocket connection
Section titled “Open a WebSocket connection”GET /api/v1/notes/sockets/{socketId}
Upgrades the HTTP connection to a WebSocket using a previously initialized socket ID. Once upgraded, send and receive messages as text frames.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
socketId | path | string | Yes | The socket ID returned by POST /api/v1/notes/sockets |
curl -i -X GET "https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com/api/v1/notes/sockets/{socketId}" \ -H "Authorization: Bearer <token>" \ -H "Connection: Upgrade" \ -H "Upgrade: websocket"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.sockets.open(socketId);{ "message": "Socket not found", "code": "SOCKET_NOT_FOUND", "details": [ { "path": "/socketId", "message": "No active session matches the provided socketId" } ]}{ "message": "WebSocket upgrade failed", "code": "UPGRADE_FAILED"}The full WebSocket URL to connect to from a browser or Node.js client is:
wss://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com/api/v1/notes/sockets/{socketId}
Mutations
Section titled “Mutations”Sync client mutations
Section titled “Sync client mutations”POST /api/v1/notes/notebooks/{notebookId}/mutations
Processes a batch of client-side mutations (node CRUD, reactions, interactions, document updates) and returns a per-mutation status result for each entry.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
notebookId | path | string | Yes | The notebook receiving the mutations |
Request Body
Section titled “Request Body”| Name | Type | Required | Description |
|---|---|---|---|
mutations | array | Yes | Ordered list of mutations to apply. Maximum 500 entries per request. Each entry follows the envelope shape below. |
Each entry in mutations carries the following envelope fields:
| Name | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Client-generated mutation ID, echoed back in the response |
createdAt | string | Yes | Client-side ISO 8601 timestamp for the mutation |
type | string | Yes | Discriminator selecting the payload shape (see below) |
data | object | Yes | Payload matching the selected type |
The supported type values and their required data fields are:
node.create
| Field | Type | Required | Description |
|---|---|---|---|
data.nodeId | string | Yes | Identifier of the new node |
data.updateId | string | Yes | Client-generated update ID |
data.createdAt | string | Yes | ISO 8601 creation timestamp |
data.data | string | Yes | Encoded node payload |
node.update
| Field | Type | Required | Description |
|---|---|---|---|
data.nodeId | string | Yes | Identifier of the node being updated |
data.updateId | string | Yes | Client-generated update ID |
data.data | string | Yes | Encoded update payload |
data.createdAt | string | Yes | ISO 8601 update timestamp |
node.delete
| Field | Type | Required | Description |
|---|---|---|---|
data.nodeId | string | Yes | Identifier of the node being deleted |
data.rootId | string | Yes | Root node ID containing the target |
data.deletedAt | string | Yes | ISO 8601 deletion timestamp |
node.reaction.create
| Field | Type | Required | Description |
|---|---|---|---|
data.nodeId | string | Yes | Node receiving the reaction |
data.reaction | string | Yes | Reaction identifier (for example, a short code) |
data.rootId | string | Yes | Root node ID containing the target |
data.createdAt | string | Yes | ISO 8601 timestamp of the reaction |
node.reaction.delete
| Field | Type | Required | Description |
|---|---|---|---|
data.nodeId | string | Yes | Node whose reaction is being removed |
data.reaction | string | Yes | Reaction identifier being removed |
data.rootId | string | Yes | Root node ID containing the target |
data.deletedAt | string | Yes | ISO 8601 timestamp of the removal |
node.interaction.seen
| Field | Type | Required | Description |
|---|---|---|---|
data.nodeId | string | Yes | Node being marked as seen |
data.collaboratorId | string | Yes | Collaborator who saw the node |
data.seenAt | string | Yes | ISO 8601 timestamp of the seen event |
node.interaction.opened
| Field | Type | Required | Description |
|---|---|---|---|
data.nodeId | string | Yes | Node being marked as opened |
data.collaboratorId | string | Yes | Collaborator who opened the node |
data.openedAt | string | Yes | ISO 8601 timestamp of the opened event |
document.update
| Field | Type | Required | Description |
|---|---|---|---|
data.documentId | string | Yes | Identifier of the document being updated |
data.updateId | string | Yes | Client-generated update ID |
data.data | string | Yes | Encoded document update payload |
data.createdAt | string | Yes | ISO 8601 update timestamp |
curl -X POST "https://{projectId}-{containerId}-notes-1.{server}.containers.hoody.com/api/v1/notes/notebooks/{notebookId}/mutations" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "mutations": [ { "id": "m-9f3a1b", "createdAt": "2026-01-15T12:00:00.000Z", "type": "node.create", "data": { "nodeId": "n-aaaa1111bbbb2222cccc3333", "updateId": "u-7c1d2e", "createdAt": "2026-01-15T12:00:00.000Z", "data": "eyJ0ZXh0IjoiSGVsbG8sIHdvcmxkISJ9" } }, { "id": "m-7b2c4d", "createdAt": "2026-01-15T12:00:05.000Z", "type": "node.reaction.create", "data": { "nodeId": "n-aaaa1111bbbb2222cccc3333", "reaction": ":wave:", "rootId": "n-root1111aaaa2222bbbb3333", "createdAt": "2026-01-15T12:00:05.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.mutations.sync(notebookId, { mutations: [ { id: 'm-9f3a1b', createdAt: '2026-01-15T12:00:00.000Z', type: 'node.create', data: { nodeId: 'n-aaaa1111bbbb2222cccc3333', updateId: 'u-7c1d2e', createdAt: '2026-01-15T12:00:00.000Z', data: 'eyJ0ZXh0IjoiSGVsbG8sIHdvcmxkISJ9', }, }, { id: 'm-7b2c4d', createdAt: '2026-01-15T12:00:05.000Z', type: 'node.reaction.create', data: { nodeId: 'n-aaaa1111bbbb2222cccc3333', reaction: ':wave:', rootId: 'n-root1111aaaa2222bbbb3333', createdAt: '2026-01-15T12:00:05.000Z', }, }, ],});{ "results": [ { "id": "m-9f3a1b", "status": "applied" }, { "id": "m-7b2c4d", "status": "applied" } ]}