<!--
hoody-notes Subskill (http)
Auto-generated by Hoody Skills Generator
Generated: 2026-06-20T00:40:08.099Z
Model: mimo-v2.5-pro + fixer:z-ai/glm-5.1
Mode: http


Tokens: 15990

DO NOT EDIT MANUALLY - Changes will be overwritten on next generation
-->

# hoody-notes Subskill

## Overview

### What is hoody-notes?

hoody-notes is a collaborative document and knowledge management service within the Hoody ecosystem. It provides a rich, block-based document system organized into notebooks, nodes, and hierarchical content structures. Think of it as a programmable Notion-like backend that enables agents to create, read, update, and manage structured content with full collaboration support.

### Core Capabilities

- **Notebooks**: Top-level containers that group related content. Each notebook supports multiple node types.
- **Nodes**: The fundamental content unit. Types include `page`, `section`, `channel`, `message`, `database`, and `record`.
- **Documents**: Rich-text block-based content attached to nodes. Supports paragraphs, headings, lists, code blocks, and drawing blocks.
- **Databases**: Structured record storage within notebooks. Records have typed fields, filterable/sortable via API.
- **Files**: Resumable file uploads via TUS protocol. Attach files to notebooks.
- **Collaboration**: Per-node collaborators with role-based access. Reactions, comments, and interaction tracking.
- **Versioning**: Snapshot-based document versioning with restore capability.
- **Real-time**: WebSocket support for live collaboration via socket sessions.

### When to Use hoody-notes

| Scenario | Use hoody-notes |
|----------|-----------------|
| Store structured knowledge for retrieval | Create notebooks + pages, write documents |
| Build a searchable FAQ or knowledge base | Create database with records, use search endpoint |
| Generate reports or summaries for users | Create pages, write documents, export as HTML |
| Track collaborative annotations | Use comments, reactions, collaborators |
| Upload and manage file attachments | Use TUS upload endpoints |
| Implement undo/redo or audit trails | Use version endpoints |

### Service Architecture

```
Notebook (top-level container)
├── Nodes (hierarchical)
│   ├── Section
│   │   └── Page
│   │       ├── Document (rich-text blocks)
│   │       ├── Collaborators
│   │       ├── Comments
│   │       ├── Reactions
│   │       └── Versions
│   ├── Database
│   │   └── Records (structured data)
│   └── Channel
│       └── Message (chat-style content)
├── Files (uploaded binaries)
└── Users (notebook members with roles)
```



## Common Workflows

### Accessing the Service

All requests use the Hoody Kit service base URL:

```
https://{projectId}-{containerId}-notes-{serviceId}.{node}.containers.hoody.com
```

Replace `{projectId}`, `{containerId}`, `{serviceId}`, and `{node}` with your deployment values. Authentication is handled automatically by the Hoody Proxy layer.

### 1. Health Check and Identity

#### Check Service Health

```
curl -s -X GET "${BASE_URL}/api/v1/notes/health"
```

Response includes service identity, build timestamps, memory usage, and process info:

```
{
  "status": "ok",
  "service": "notes",
  "buildTimestamp": "2025-01-15T10:30:00Z",
  "startTimestamp": "2025-01-15T12:00:00Z",
  "memory": { "rss": 52428800, "heapUsed": 20971520 },
  "pid": 1234,
  "peerAddress": "10.0.0.1"
}
```

#### Get Current User Identity

```
curl -s -X GET "${BASE_URL}/api/v1/notes/me"
```

This auto-provisions the user and their default notebook on first call:

```
{
  "userId": "usr_abc123",
  "username": "agent-42",
  "role": "owner",
  "notebookId": "nb_xyz789"
}
```

**Important**: Call `/me` before any other operation to ensure your user and default notebook exist.

### 2. Notebook Management

#### List All Notebooks

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks"
```

Returns all notebooks where the current user has an active membership (excludes `role: "none"` and inactive status).

#### Create a New Notebook

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Research Notes"
}'
```

The `name` field is required. Optional fields include `description` and `avatarId`.

#### Get Notebook Details

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}"
```

Returns metadata including name, description, avatar, status, and your role.

#### Update a Notebook

Only notebook owners can update:

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Updated Research Notes"
}'
```

PATCH supports partial updates — send only the fields you want to change (`name`, `description`, `avatarId`).

#### Delete a Notebook

**Warning**: Permanently deletes the notebook and ALL contained data. Only owners can delete.

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}"
```

### 3. Node Operations

#### List Nodes in a Notebook

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes"
```

Supports query parameters for filtering:
- `type`: Filter by node type (e.g., `page`, `section`, `database`)
- `parentId`: Filter by parent node ID
- `rootId`: Filter by root node ID
- `limit` / `offset`: Pagination

#### Create a Node

Create a page node:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "page",
  "attributes": {
    "name": "My New Page"
  }
}'
```

Create a section node:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "section",
  "attributes": {
    "name": "Documentation"
  }
}'
```

Create a database node:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "database",
  "attributes": {
    "name": "Task Tracker"
  }
}'
```

The `type` and `attributes` fields are both required.

#### Get a Node by ID

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}"
```

#### Get a Node by Alias

Resolve a page by its safe alias (URL-friendly slug):

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/alias/{alias}"
```

#### Update a Node

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}" \
  -H "Content-Type: application/json" \
  -d '{
  "attributes": {
    "name": "Renamed Page"
  }
}'
```

**Note**: `type` and `parentId` cannot be changed after creation. Only `attributes` is mutable.

#### Delete a Node

Permanently deletes the node and all associated data (documents, files, reactions):

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}"
```

#### List Child Nodes

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/children"
```

Returns paginated direct children. Use `limit` and `offset` for pagination.

### 4. Document Operations

#### Read a Document

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document"
```

Supports query parameters:
- `blockIds`: Comma-separated block IDs to filter
- `startLine` / `endLine`: Line range queries

#### Replace a Document (PUT)

Fully replaces existing document content:

```
curl -s -X PUT "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document" \
  -H "Content-Type: application/json" \
  -d '{
  "content": {
    "blocks": [
      {
        "id": "blk_001",
        "type": "paragraph",
        "content": [
          {
            "type": "text",
            "text": "Hello world"
          }
        ]
      }
    ]
  }
}'
```

The `content` field (object) is required.

#### Merge Content into a Document (PATCH)

Merges at the top level. Existing blocks are preserved unless overwritten by matching ID:

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document" \
  -H "Content-Type: application/json" \
  -d '{
  "content": {
    "blocks": [
      {
        "id": "blk_002",
        "type": "heading",
        "content": [
          {
            "type": "text",
            "text": "Section Title"
          }
        ]
      }
    ]
  }
}'
```

The `content` field (object) is required.

#### Append Blocks to a Document

Appends blocks to the END of a document. The server assigns all IDs — do not provide `id`, `parentId`, or `index`:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document/append" \
  -H "Content-Type: application/json" \
  -d '{
  "blocks": [
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "Appended content here.",
          "marks": []
        }
      ]
    }
  ]
}'
```

Required fields: `blocks` (array), each block's `content[].type` (string), `content[].text` (string), and `content[].marks[].type` (string) when marks are present.

#### Render a Drawing Block as SVG

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/blocks/{blockId}/svg"
```

Optional query parameters: `backgroundColor`, `scale`.

### 5. Database and Record Operations

#### List Records in a Database

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records"
```

Supports JSON-encoded `filters` and `sorts` query parameters, plus `limit`/`offset` pagination.

#### Search Records by Name

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records/search?q=searchTerm"
```

Supports `excludeIds` parameter to omit specific records from results.

#### Create a Record

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Task: Fix login bug",
  "fields": {
    "status": "open",
    "priority": "high"
  }
}'
```

#### Get a Single Record

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records/{recordId}"
```

#### Update a Record

Fields are merged with existing values (partial update):

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records/{recordId}" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Task: Fix login bug (in progress)",
  "fields": {
    "status": "in-progress"
  }
}'
```

#### Delete a Record

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records/{recordId}"
```

### 6. File Upload and Download (TUS Protocol)

The file system uses the [TUS resumable upload protocol](https://tus.io/).

#### Create Upload Session

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Returns upload URL and metadata for subsequent chunk uploads.

#### Upload File Chunk

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus" \
  -H "Content-Type: application/offset+octet-stream" \
  -H "Upload-Offset: 0" \
  -d @localfile.dat
```

#### Check Upload Progress

```
curl -s -X HEAD "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus"
```

Returns current `Upload-Offset` header indicating bytes received.

#### Abort an Upload

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/files/{fileId}/tus"
```

#### List Files in a Notebook

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/files"
```

Returns paginated list with `limit`/`offset` support.

#### Download a File

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/files/{fileId}" \
  -o downloaded_file.dat
```

Returns binary data with the original content type.

### 7. Collaboration

#### List Collaborators on a Node

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/collaborators"
```

#### Add a Collaborator

Requires admin permission on the node:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/collaborators" \
  -H "Content-Type: application/json" \
  -d '{
  "userId": "usr_other123",
  "role": "editor"
}'
```

#### Change Collaborator Role

Requires admin permission:

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/collaborators/{collaboratorId}" \
  -H "Content-Type: application/json" \
  -d '{
  "role": "viewer"
}'
```

#### Remove a Collaborator

Requires admin permission:

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/collaborators/{collaboratorId}"
```

### 8. Comments

#### List Comments on a Node

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments"
```

#### Get Comment Anchors (Lightweight)

Returns thread anchor metadata for rendering comment decorations:

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comment-anchors"
```

#### Create a Comment

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments" \
  -H "Content-Type: application/json" \
  -d '{
  "content": "This section needs clarification.",
  "anchor": {
    "blockId": "blk_001",
    "startOffset": 0,
    "endOffset": 10
  }
}'
```

#### Edit a Comment

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}" \
  -H "Content-Type: application/json" \
  -d '{
  "content": "Updated: this section is now clear."
}'
```

#### Delete a Comment

Deletes the comment and all its replies:

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}"
```

#### Reanchor a Comment Thread

Move a comment thread to a new document position:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/comments/{commentId}/reanchor" \
  -H "Content-Type: application/json" \
  -d '{
  "anchor": {
    "blockId": "blk_005",
    "startOffset": 5,
    "endOffset": 20
  }
}'
```

### 9. Reactions

#### List Reactions on a Node

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/reactions"
```

#### Add a Reaction

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/reactions" \
  -H "Content-Type: application/json" \
  -d '{
  "emoji": "👍"
}'
```

#### Remove a Reaction

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/reactions/{reaction}"
```

The `{reaction}` path parameter is the emoji string (URL-encoded if needed).

### 10. Interactions (Tracking)

#### Record "Seen" Interaction

Tracks first-seen and last-seen timestamps:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/interactions/seen" \
  -H "Content-Type: application/json" \
  -d '{}'
```

#### Record "Opened" Interaction

Tracks first-opened and last-opened timestamps:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/interactions/opened" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### 11. Versioning

#### List Versions

Returns all document versions ordered by revision descending:

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/versions"
```

#### Capture a Version Snapshot

Saves current document content as a new version:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/versions" \
  -H "Content-Type: application/json" \
  -d '{}'
```

#### Get a Specific Version

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/versions/{versionId}"
```

#### Restore a Version

Replaces current document content with the version's content:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/versions/{versionId}/restore" \
  -H "Content-Type: application/json" \
  -d '{}'
```

#### Delete a Version

```
curl -s -X DELETE "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/versions/{versionId}"
```

### 12. User Management

#### Create Users in a Notebook

Add one or more users by username:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/users" \
  -H "Content-Type: application/json" \
  -d '{
  "usernames": ["agent-alpha", "agent-beta"]
}'
```

Returns created users and any errors for failed usernames.

#### Change a User's Notebook Role

Requires owner or admin permission:

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/users/{userId}/role" \
  -H "Content-Type: application/json" \
  -d '{
  "role": "admin"
}'
```

### 13. Avatars

#### Upload an Avatar

Upload a JPEG, PNG, or WebP image. It is resized to 500×500 and converted to JPEG:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/avatars" \
  -H "Content-Type: image/png" \
  --data-binary @avatar.png
```

#### Download an Avatar

Returns JPEG binary data:

```
curl -s -X GET "${BASE_URL}/api/v1/notes/avatars/{avatarId}" \
  -o avatar.jpg
```

### 14. WebSocket Sessions

#### Initialize a Socket

Create a socket session and receive a socket ID:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/sockets" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Response includes a `socketId` for the next step.

#### Open WebSocket Connection

Upgrade to WebSocket using the socket ID:

```
GET /api/v1/notes/sockets/{socketId} HTTP/1.1
Upgrade: websocket
Connection: Upgrade
```

This endpoint upgrades the HTTP connection to WebSocket. Use a WebSocket client library — `curl` does not support WebSocket upgrades.

---

## Advanced Operations

### 1. Complete Knowledge Base Setup

A full workflow to create a knowledge base from scratch:

**Step 1: Ensure identity exists**

```
curl -s -X GET "${BASE_URL}/api/v1/notes/me"
```

Save the returned `notebookId` for subsequent calls.

**Step 2: Create organizational sections**

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "section",
  "attributes": {
    "name": "Getting Started"
  }
}'
```

Save the returned node ID as `sectionId`.

**Step 3: Create a page under the section**

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "page",
  "attributes": {
    "name": "Installation Guide",
    "parentId": "{sectionId}"
  }
}'
```

Save the returned node ID as `pageId`.

**Step 4: Write document content**

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{pageId}/document/append" \
  -H "Content-Type: application/json" \
  -d '{
  "blocks": [
    {
      "type": "heading",
      "content": [
        {
          "type": "text",
          "text": "Installation Steps",
          "marks": []
        }
      ]
    },
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "Follow these steps to install the service.",
          "marks": []
        }
      ]
    },
    {
      "type": "codeBlock",
      "content": [
        {
          "type": "text",
          "text": "npm install @hoody/notes-client",
          "marks": []
        }
      ]
    }
  ]
}'
```

**Step 5: Capture initial version**

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{pageId}/versions" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**Step 6: Verify the setup**

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{sectionId}/children"
```

Should list the newly created page.

### 2. Structured Database Workflow

Create a database with typed records:

**Step 1: Create a database node**

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes" \
  -H "Content-Type: application/json" \
  -d '{
  "type": "database",
  "attributes": {
    "name": "Bug Tracker"
  }
}'
```

Save `databaseId` (same as the node ID returned).

**Step 2: Add records**

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Auth service returns 500 on expired tokens",
  "fields": {
    "severity": "critical",
    "assignee": "agent-alpha",
    "status": "open",
    "dueDate": "2025-02-01"
  }
}'
```

**Step 3: Search records**

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records/search?q=auth"
```

**Step 4: Update a record**

```
curl -s -X PATCH "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/databases/{databaseId}/records/{recordId}" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Auth service returns 500 on expired tokens",
  "fields": {
    "status": "in-progress",
    "assignee": "agent-beta"
  }
}'
```

### 3. Batch Mutations

Process multiple operations in a single request for efficiency:

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/mutations" \
  -H "Content-Type: application/json" \
  -d '{
  "mutations": [
    {
      "type": "node.create",
      "payload": {
        "type": "page",
        "attributes": { "name": "Batch Page 1" }
      }
    },
    {
      "type": "node.create",
      "payload": {
        "type": "page",
        "attributes": { "name": "Batch Page 2" }
      }
    },
    {
      "type": "reaction.add",
      "payload": {
        "nodeId": "{existingNodeId}",
        "emoji": "⭐"
      }
    }
  ]
}'
```

Returns per-mutation status results, enabling partial failure handling.

### 4. Document Export Workflow

Export a document as static HTML:

**Step 1: Create an export ticket**

```
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/export-ticket" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Returns a short-lived `ticket` token.

**Step 2: Fetch HTML output**

```
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document?output=html&ticket={ticket}"
```

**Note**: The ticket is required for HTML output and expires after a short period. Generate a new ticket for each export.

### 5. Version-Restore Pattern

Safely update a document with rollback capability:

```
# Step 1: Snapshot current state
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/versions" \
  -H "Content-Type: application/json" \
  -d '{}'

# Step 2: Apply changes
curl -s -X PUT "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document" \
  -H "Content-Type: application/json" \
  -d '{
  "content": {
    "blocks": [
      {
        "id": "blk_new",
        "type": "paragraph",
        "content": [
          {
            "type": "text",
            "text": "New content replacing the old."
          }
        ]
      }
    ]
  }
}'

# Step 3: Verify the update
curl -s -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document"

# Step 4: If something went wrong, restore
curl -s -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/versions/{versionId}/restore" \
  -H "Content-Type: application/json" \
  -d '{}'
```

### Error Recovery Patterns

#### Check Node Existence Before Operations

```
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
  -X GET "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}")

if [ "$HTTP_CODE" = "200" ]; then
  echo "Node exists, proceeding..."
elif [ "$HTTP_CODE" = "404" ]; then
  echo "Node not found, create it first."
fi
```

#### Retry Pattern for Transient Failures

```
MAX_RETRIES=3
RETRY_DELAY=2

for i in $(seq 1 $MAX_RETRIES); do
  RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "${BASE_URL}/api/v1/notes/notebooks/{notebookId}/nodes/{nodeId}/document/append" \
    -H "Content-Type: application/json" \
    -d '{
      "blocks": [
        {
          "type": "paragraph",
          "content": [
            {
              "type": "text",
              "text": "Resilient append.",
              "marks": []
            }
          ]
        }
      ]
    }')

  if [ "$RESPONSE" = "200" ] || [ "$RESPONSE" = "201" ]; then
    echo "Success on attempt $i"
    break
  fi

  echo "Attempt $i failed (HTTP $RESPONSE), retrying in ${RETRY_DELAY}s..."
  sleep $RETRY_DELAY
done
```

### Performance Considerations

| Concern | Recommendation |
|---------|---------------|
| Large document reads | Use `blockIds` parameter to fetch only needed blocks |
| Node listings | Use `limit`/`offset` pagination; avoid fetching all at once |
| Batch operations | Use `/mutations` endpoint for bulk creates/updates |
| File uploads | Use TUS protocol for large files — supports resume on failure |
| Version history | Prune old versions periodically to reduce storage |
| Database queries | Use `filters` parameter server-side rather than client-side filtering |

---

## Quick Reference

### Essential Parameters

| Parameter | Used By | Description |
|-----------|---------|-------------|
| `notebookId` | Most endpoints | Notebook identifier (from `/me` or list) |
| `nodeId` | Node + document endpoints | Node identifier |
| `type` | Node creation | Node type: `page`, `section`, `channel`, `message`, `database`, `record` |
| `limit` / `offset` | List endpoints | Pagination controls |
| `blockIds` | Document read | Comma-separated block IDs to filter |
| `q` | Record search | Search query string |
| `filters` | Record list | JSON-encoded filter object |
| `sorts` | Record list | JSON-encoded sort object |

### Typical Response Format

Successful responses return JSON objects or arrays. Standard envelope:

```
{
  "data": {
    "id": "nb_abc123",
    "name": "My Notebook",
    "status": "active"
  }
}
```

Error responses include status code and message:

```
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Node not found"
  }
}
```

### Base URL Template

```
https://{projectId}-{containerId}-notes-{serviceId}.{node}.containers.hoody.com
```

All paths from this subskill append to this base. Authentication is automatic via Hoody Proxy — no API key headers needed for requests from within your infrastructure.

### Node Type Reference

| Type | Purpose | Has Document | Has Children |
|------|---------|--------------|--------------|
| `section` | Organizational folder | No | Yes |
| `page` | Rich-text document | Yes | Yes |
| `channel` | Communication channel | No | Yes |
| `message` | Chat message | Yes | No |
| `database` | Structured data container | No | Yes (records) |
| `record` | Database row entry | Yes | No |