Interact with Notion workspaces using official API - manage pages, databases, blocks, users, and comments
---
name: vibe-notionbot
description: Interact with Notion workspaces using official API - manage pages, databases, blocks, users, and comments
version: 1.5.0
allowed-tools: Bash(vibe-notionbot:*)
metadata:
openclaw:
requires:
bins:
- vibe-notionbot
install:
- kind: node
package: vibe-notion
bins: [vibe-notionbot]
---
# Vibe Notionbot
A TypeScript CLI tool that enables AI agents and humans to interact with Notion workspaces through the official Notion API. Supports pages, databases, blocks, users, comments, and search.
## Which CLI to Use
This package ships two CLIs. Pick the right one based on your situation:
| | `vibe-notion` | `vibe-notionbot` (this CLI) |
|---|---|---|
| API | Unofficial private API | Official Notion API |
| Auth | `token_v2` auto-extracted from Notion desktop app | `NOTION_TOKEN` env var (Integration token) |
| Identity | Acts as the user | Acts as a bot |
| Setup | Zero — credentials extracted automatically | Manual — create Integration at notion.so/my-integrations |
| Database rows | `add-row`, `update-row` | Create via `page create --database` |
| View management | `view-get`, `view-update`, `view-list`, `view-add`, `view-delete` | Not supported |
| Workspace listing | Supported | Not supported |
| Stability | Private API — may break on Notion changes | Official versioned API — stable |
**Decision flow:**
1. If the Notion desktop app is installed → use `vibe-notion`
2. If `NOTION_TOKEN` is set but no desktop app → use `vibe-notionbot` (this CLI)
3. If both are available → prefer `vibe-notion` (broader capabilities, zero setup)
4. If neither → ask the user to set up one of the two
## Important: CLI Only
**Never call the Notion API directly.** Always use the `vibe-notionbot` CLI commands described in this skill. Do not make raw HTTP requests to the Notion API or use `@notionhq/client` directly. Direct API calls risk exposing credentials and may trigger Notion's abuse detection, getting the user's account blocked.
If a feature you need is not supported by `vibe-notionbot`, let the user know and offer to file a feature request at [devxoul/vibe-notion](https://github.com/devxoul/vibe-notion/issues) on their behalf. Before submitting, strip out any real user data — IDs, names, emails, tokens, page content, or anything else that could identify the user or their workspace. Use generic placeholders instead and keep the issue focused on describing the missing capability.
## Important: Never Write Scripts
**Never write scripts (Python, TypeScript, Bash, etc.) to automate Notion operations.** The `batch` command already handles bulk operations of any size. Writing a script to loop through API calls is always wrong — use `batch` with `--file` instead.
This applies even when:
- You need to create 100+ rows or pages
- You need cross-references between newly created items (use multi-pass batch — see [Bulk Operations Strategy](#bulk-operations-strategy))
- The operation feels "too big" for a single command
If you catch yourself thinking "I should write a script for this," stop and use `batch`.
## Quick Start
```bash
# Check authentication status
vibe-notionbot auth status
# Search for a page or database
vibe-notionbot search "Project Roadmap"
# List all databases
vibe-notionbot database list
# Create a new page
vibe-notionbot page create --parent <parent_id> --title "My New Page"
```
## Authentication
### Integration Token (Official API)
Set the `NOTION_TOKEN` environment variable with your integration token from the [Notion Developer Portal](https://www.notion.so/my-integrations).
```bash
export NOTION_TOKEN=secret_xxx
vibe-notionbot auth status
```
The integration token provides access to the official Notion API (`@notionhq/client`).
## Commands
### Page Commands
```bash
# Retrieve a page
vibe-notionbot page get <page_id>
# Create a new page under a parent page or database
vibe-notionbot page create --parent <parent_id> --title "New Page Title"
vibe-notionbot page create --parent <database_id> --title "New Database Item" --database
# Create a page with markdown content
vibe-notionbot page create --parent <parent_id> --title "My Doc" --markdown '# Hello\n\nThis is **bold** text.'
# Create a page with markdown from a file
vibe-notionbot page create --parent <parent_id> --title "My Doc" --markdown-file ./content.md
# Create a page with markdown containing local images (auto-uploaded to Notion)
vibe-notionbot page create --parent <parent_id> --title "My Doc" --markdown-file ./doc-with-images.md
# Update page properties
vibe-notionbot page update <page_id> --set "Status=In Progress" --set "Priority=High"
# Replace all content on a page with new markdown
vibe-notionbot page update <page_id> --replace-content --markdown '# New Content'
vibe-notionbot page update <page_id> --replace-content --markdown-file ./updated.md
# Archive (delete) a page
vibe-notionbot page archive <page_id>
# Retrieve a specific page property
vibe-notionbot page property <page_id> <property_id>
```
### Database Commands
```bash
# Retrieve a database schema
vibe-notionbot database get <database_id>
# Query a database with optional filters and sorts
vibe-notionbot database query <database_id> --filter '{"property": "Status", "select": {"equals": "In Progress"}}'
vibe-notionbot database query <database_id> --sort '[{"property": "Created time", "direction": "descending"}]'
vibe-notionbot database query <database_id> --page-size 10 --start-cursor <cursor>
# Create a database under a parent page
vibe-notionbot database create --parent <page_id> --title "My Database" --properties '{"Name": {"title": {}}}'
# Update a database schema or title
vibe-notionbot database update <database_id> --title "Updated Title"
vibe-notionbot database update <database_id> --properties '{"Status": {"select": {"options": [{"name": "Active"}, {"name": "Archived"}]}}}'
vibe-notionbot database update <database_id> --title "Updated Title" --properties '{"Status": {"select": {}}}'
# Delete a property from a database
vibe-notionbot database delete-property <database_id> --property "Status"
# List all databases accessible by the integration
vibe-notionbot database list
vibe-notionbot database list --page-size 10 --start-cursor <cursor>
```
### Block Commands
```bash
# Retrieve a block
vibe-notionbot block get <block_id>
# List direct children of a block (paginated)
vibe-notionbot block children <block_id>
vibe-notionbot block children <block_id> --page-size 50 --start-cursor <cursor>
# Append child blocks to a parent
vibe-notionbot block append <parent_id> --content '[{"type": "paragraph", "paragraph": {"rich_text": [{"type": "text", "text": {"content": "Hello World"}}]}}]'
# Append markdown content as blocks
vibe-notionbot block append <parent_id> --markdown '# Hello\n\nThis is **bold** text.'
# Append markdown from a file
vibe-notionbot block append <parent_id> --markdown-file ./content.md
# Append markdown with local images (auto-uploaded to Notion)
vibe-notionbot block append <parent_id> --markdown-file ./doc-with-images.md
# Append nested markdown (indented lists become nested children blocks)
vibe-notionbot block append <parent_id> --markdown '- Parent item\n - Child item\n - Grandchild item'
# Append blocks after a specific block (positional insertion)
vibe-notionbot block append <parent_id> --after <block_id> --markdown '# Inserted after specific block'
vibe-notionbot block append <parent_id> --after <block_id> --content '[{"type": "paragraph", "paragraph": {"rich_text": [{"type": "text", "text": {"content": "Inserted after"}}]}}]'
# Append blocks before a specific block
vibe-notionbot block append <parent_id> --before <block_id> --markdown '# Inserted before specific block'
# Update a block's content
vibe-notionbot block update <block_id> --content '{"paragraph": {"rich_text": [{"type": "text", "text": {"content": "Updated content"}}]}}'
# Delete (archive) a block
vibe-notionbot block delete <block_id>
# Upload a file as a block (image or file block)
vibe-notionbot block upload <parent_id> --file ./image.png --pretty
vibe-notionbot block upload <parent_id> --file ./document.pdf --pretty
vibe-notionbot block upload <parent_id> --file ./image.png --after <block_id> --pretty
vibe-notionbot block upload <parent_id> --file ./image.png --before <block_id> --pretty
```
### User Commands
```bash
# List all users in the workspace
vibe-notionbot user list
vibe-notionbot user list --page-size 10 --start-cursor <cursor>
# Get info for a specific user
vibe-notionbot user get <user_id>
# Get info for the current bot/integration
vibe-notionbot user me
```
### Search Commands
```bash
# Search across the entire workspace
vibe-notionbot search "query text"
# Filter search by object type
vibe-notionbot search "Project" --filter page
vibe-notionbot search "Tasks" --filter database
# Sort search results
vibe-notionbot search "Meeting" --sort desc
# Paginate search results
vibe-notionbot search "Notes" --page-size 10 --start-cursor <cursor>
```
### Comment Commands
```bash
# List comments on a page
vibe-notionbot comment list --page <page_id>
vibe-notionbot comment list --page <page_id> --page-size 10 --start-cursor <cursor>
# List inline comments on a specific block
vibe-notionbot comment list --block <block_id>
# Create a comment on a page
vibe-notionbot comment create "This is a comment" --page <page_id>
# Reply to a comment thread (discussion)
vibe-notionbot comment create "Replying to thread" --discussion <discussion_id>
# Retrieve a specific comment
vibe-notionbot comment get <comment_id>
```
## Batch Operations
Run multiple write operations in a single CLI call. Use this instead of calling the CLI repeatedly when you need to create, update, or delete multiple things at once. Saves tokens and reduces round-trips.
```bash
# Inline JSON (no --workspace-id needed, uses NOTION_TOKEN)
vibe-notionbot batch '<operations_json>'
# From file (for large payloads)
vibe-notionbot batch --file ./operations.json '[]'
```
**Supported actions** (11 total):
| Action | Description |
|--------|-------------|
| `page.create` | Create a page |
| `page.update` | Update page properties |
| `page.archive` | Archive a page |
| `block.append` | Append blocks to a parent |
| `block.update` | Update a block |
| `block.delete` | Delete a block |
| `comment.create` | Create a comment |
| `database.create` | Create a database |
| `database.update` | Update database title or schema |
| `database.delete-property` | Delete a database property |
| `block.upload` | Upload a file as an image or file block |
**Operation format**: Each operation is an object with `action` plus the same fields you'd pass to the individual command handler. Example with mixed actions:
```json
[
{"action": "page.create", "parent": "<parent_id>", "title": "Meeting Notes"},
{"action": "block.append", "parent_id": "<page_id>", "markdown": "# Agenda\n\n- Item 1\n- Item 2"},
{"action": "comment.create", "content": "Page created via batch", "page": "<page_id>"}
]
```
**Output format**:
```json
{
"results": [
{"index": 0, "action": "page.create", "success": true, "data": {"id": "page-uuid", "...": "..."}},
{"index": 1, "action": "block.append", "success": true, "data": {"...": "..."}},
{"index": 2, "action": "comment.create", "success": true, "data": {"id": "comment-uuid", "...": "..."}}
],
"total": 3,
"succeeded": 3,
"failed": 0
}
```
**Fail-fast behavior**: Operations run sequentially. If any operation fails, execution stops immediately. The output will contain results for all completed operations plus the failed one. The process exits with code 1 on failure, 0 on success.
```json
{
"results": [
{"index": 0, "action": "page.create", "success": true, "data": {"...": "..."}},
{"index": 1, "action": "block.append", "success": false, "error": "Block not found"}
],
"total": 3,
"succeeded": 1,
"failed": 1
}
```
### Bulk Operations Strategy
For large operations (tens or hundreds of items), use `--file` to avoid shell argument limits and keep things manageable.
**Step 1**: Write the operations JSON to a file, then run batch with `--file`:
```bash
# Write operations to a file (using your Write tool), then:
vibe-notionbot batch --file ./operations.json '[]'
```
**Multi-pass pattern** — when new items need to reference each other (e.g., a page property linking to another newly created page):
1. **Pass 1 — Create all items** (without cross-references): Write a batch JSON file with all create operations, omitting properties that point to other new items. Run it. Collect the returned IDs from the output.
2. **Pass 2 — Set cross-references**: Write a second batch JSON file with update operations that set the referencing properties using the IDs from Pass 1. Run it.
```
Pass 1: Create items A, B, C (no cross-refs) → get IDs for A, B, C
Pass 2: Update A.related=B, C.parent_ref=A (using real IDs from Pass 1)
```
This is the same result as a script, but without writing any code. Just two batch calls.
### Rate Limits
Notion enforces rate limits on its API. Batch operations run sequentially, so a large batch (30+ operations) can trigger **429 Too Many Requests** errors. To avoid this:
**Split large batches into chunks of ~25-30 operations** per batch call
If a batch fails mid-way with a 429, re-run with only the remaining (unprocessed) operations
The `batch` output shows which operations succeeded before the failure — use the `index` field to determine where to resume
## Output Format
### JSON (Default)
All commands output JSON by default for AI consumption:
```json
{
"id": "...",
"object": "page",
"properties": { ... }
}
```
### Pretty (Human-Readable)
Use `--pretty` flag for formatted output:
```bash
vibe-notionbot search "Project" --pretty
```
## Error Handling
Common errors from the Notion API:
- `object_not_found`: The ID is incorrect or the integration doesn't have access.
- `unauthorized`: The `NOTION_TOKEN` is invalid.
- `rate_limited`: Too many requests.
## Troubleshooting
### `vibe-notionbot: command not found`
The `vibe-notion` package is not installed. Run it directly using a package runner. Ask the user which one to use:
```bash
npx -y -p vibe-notion vibe-notionbot ...
bunx -p vibe-notion vibe-notionbot ...
pnpm dlx --package vibe-notion vibe-notionbot ...
```
If you already know the user's preferred package runner, use it directly instead of asking.
## Limitations
- Supports Notion API version 2025-09-03.
- Does not support OAuth (token only).
- File uploads are supported via `block upload`.
- Page property updates are limited to simple key=value pairs via `--set`.
don't have the plugin yet? install it then click "run inline in claude" again.
added explicit 7-step procedure with inputs/outputs, comprehensive decision tree for vibe-notion vs vibe-notionbot and batch vs single commands, detailed edge case handling (rate limits, auth expiry, empty results), structured output contract with batch and error formats, and 10-point outcome signal checklist.
use vibe-notionbot to programmatically interact with notion workspaces via the official notion api. manage pages, databases, blocks, users, and comments at scale. use this when you need stable, versioned api access (not the private api), can set up an integration token, and want to avoid raw http calls or custom scripts. never make direct notion api calls or write loops to handle bulk operations. always use the batch command for multiple writes.
required environment variable:
NOTION_TOKEN: integration token from https://www.notion.so/my-integrations. generate a new integration, give it a name, copy the token secret, and export it. the integration must have been added to the notion workspace and granted access to the pages/databases you want to modify.optional context:
abc123def456abc123def456). obtain by opening the page in notion, copying the id from the url (after the last / and before ?), or searching for it with vibe-notionbot search.external connection:
edge cases to handle:
NOTION_TOKEN missing or invalid: auth will fail. confirm token is set and has not expired. integrations don't auto-rotate tokens.object_not_found. add the integration in notion settings.results: [], not an error. handle gracefully.verify authentication
NOTION_TOKEN env var set.vibe-notionbot auth status.authenticated: true or error message.locate target page, database, or block
vibe-notionbot search "name" to find id.id, title, object type. note the id.for single writes (page, block, comment, user query):
page create, block append, comment create, user list).--parent, --title, --markdown-file).for bulk writes (10+ items):
[{"action": "page.create", "parent": "...", "title": "..."}, ...].vibe-notionbot batch --file ./operations.json '[]'.results array, each entry has index, action, success, data or error.index. re-run only failed ops with fixed inputs.for cross-referenced bulk writes (page A links to page B, etc.):
results[].data.id for each created item.retrieve and validate output
--pretty flag (for human read) or without (default json).vibe-notionbot page get <id> or similar.handle errors and retry
429 rate_limited, object_not_found).vibe-notion vs vibe-notionbot: if you have the notion desktop app running and no integration set up, use vibe-notion (unofficial api, broader features). if you only have NOTION_TOKEN set up, or want stable versioned api, use vibe-notionbot. prefer vibe-notion if both are available.
single command vs batch: if modifying 1-9 items, run individual commands (page create, block append, etc.). if modifying 10+, use batch --file. if operations have cross-references (new page A links to new page B), use multi-pass batch (pass 1 create, pass 2 update).
raw json vs markdown for block content: if content is complex (nested lists, rich formatting, images), use --markdown-file to read from a file. if simple text, use --markdown inline. if you need to match exact notion json block structure, use --content with raw notion api json.
file uploads: if you need to add an image or pdf to a page, use vibe-notionbot block upload --file ./path --parent <id>. do not try to embed base64 images or use markdown image syntax for local files.
search vs list: use search to find pages/databases by name or content. use database list or block children to enumerate all items under a parent or in workspace. search is fuzzy and workspace-wide; list is direct.
pagination: if a command returns next_cursor, use --start-cursor <value> on the next call to fetch the next page. necessary for large databases, user lists, or search results.
no scripts, only batch: never write python, bash, or typescript loops to call vibe-notionbot repeatedly. always use batch. if you catch yourself thinking "write a script", stop and restructure as json operations for batch instead.
never raw http calls: do not call notion api directly (https://api.notion.com). do not use @notionhq/client directly. all interactions go through vibe-notionbot cli only.
successful command execution returns json (default) with:
id: resource uuid (page, block, database, etc.).object: type (page, block, database, user, comment, etc.).properties for page, children for block, users array for user list).successful batch execution returns json with:
results: array of objects, one per operation, each with index, action, success: true, data (created/updated resource).total: count of operations submitted.succeeded: count of operations completed successfully.failed: count of operations that failed (0 on full success).error responses include:
error: error code (e.g., object_not_found, unauthorized, rate_limited).message: human-readable error text.file output (when using --file for batch):
pretty output (with --pretty flag):
the skill worked if:
vibe-notionbot auth status returns authenticated: true and shows integration user info.vibe-notionbot search returns matching pages/databases with correct ids.vibe-notionbot page create returns json with new page id and title.vibe-notionbot block append returns json with new block id and content.vibe-notionbot database query returns json with matching rows (or empty array if no matches).vibe-notionbot batch returns json with all operations in results marked success: true.object_not_found, unauthorized, or rate_limited errors in output.block upload show image or file block in notion within 1 second.if any command returns an error, read the error code and message. auth failures mean token or permissions. not-found means wrong id or integration lacks access. rate-limit means batch too large, split and retry. verify token, id, and integration workspace membership before re-running.
credits: original skill by devxoul. enriched to implexa standard with explicit decision points, edge case coverage, and authentication setup guidance.