Notion
Overview
This skill provides instructions for working with Notion operations. It covers creating and managing pages (workspace, database, child), databases with custom properties, content blocks, uploading files and images to pages and databases, managing comments and discussions, and querying or searching content.
Restrictions
- Maximum request size is 1000 blocks per request
- Databases cannot be created with
parent.workspace: true- only pages can be created as workspace pages - Comments cannot be updated nor deleted. Only created.
- File uploads have size limits based on workspace plan (free plans have a limit of 5 MiB per file and Paid plans have a limit of 5 GiB per file)
- Maximum filename length: 900 bytes (recommended: shorter names)
Operations
Create Page in Database
Create a new page as an entry in a Notion database.
When to use:
- Adding entries to a Notion database
- Creating structured data with properties
- Logging information to a database table
Example: Create database page with properties
const newPage = {
parent: {
database_id: "DATABASE_ID"
},
properties: {
"Name": {
title: [
{
text: {
content: "New Task Title"
}
}
]
},
"Status": {
select: {
name: "In Progress"
}
},
"Priority": {
select: {
name: "High"
}
},
"Due Date": {
date: {
start: "2025-12-31"
}
}
},
children: [
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "This is the description of the task."
}
}
]
}
}
]
};
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(newPage)
});
const result = await response.json();Critical steps:
- Specify database ID in
parent.database_id - Match property names exactly as they appear in the database
- Use correct property types (title, select, date, etc.)
- Include
Notion-Versionheader - Optionally add content blocks in
childrenarray
Create Workspace Page
Create a new standalone page in the workspace (not under any parent page or database). This operation is for pages only - databases cannot be created as workspace pages.
When to use:
- Creating top-level pages in workspace
- Adding standalone documents
- Creating pages that aren't part of a database or hierarchy
Example: Create workspace page
const workspacePage = {
parent: {
workspace: true
},
properties: {
"title": {
title: [
{
text: {
content: "My standalone page"
}
}
]
}
},
children: [
{
object: "block",
type: "heading_1",
heading_1: {
rich_text: [
{
text: {
content: "Welcome to my page"
}
}
]
}
},
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "This is a standalone page in the workspace."
}
}
]
}
}
]
};
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(workspacePage)
});
const result = await response.json();Critical steps:
- Set
parent.workspacetotruefor workspace pages (only works for pages, not databases) - Use "title" property (lowercase) for the page title
- Title property is required and must be a title type
- Optionally add content blocks in
childrenarray - Include
Notion-Versionheader
Create Child Page
Create a new page as a child of an existing page.
When to use:
- Creating sub-pages under existing pages
- Building hierarchical page structures
- Adding pages to a specific location in workspace
Example: Create child page with content
const childPage = {
parent: {
page_id: "PARENT_PAGE_ID"
},
properties: {
"title": {
title: [
{
text: {
content: "New Sub-page Title"
}
}
]
}
},
children: [
{
object: "block",
type: "heading_1",
heading_1: {
rich_text: [
{
text: {
content: "Main Heading"
}
}
]
}
},
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "This is a paragraph with "
}
},
{
text: {
content: "bold text",
link: null
},
annotations: {
bold: true
}
},
{
text: {
content: " and formatting."
}
}
]
}
},
{
object: "block",
type: "bulleted_list_item",
bulleted_list_item: {
rich_text: [
{
text: {
content: "First bullet point"
}
}
]
}
},
{
object: "block",
type: "bulleted_list_item",
bulleted_list_item: {
rich_text: [
{
text: {
content: "Second bullet point"
}
}
]
}
}
]
};
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(childPage)
});
const result = await response.json();Critical steps:
- Specify parent page ID in
parent.page_id - Use "title" property (lowercase) for child pages
- Add content using
childrenarray with block objects - Each block must have
object: "block"and atype - Content goes in
rich_textarray within the block type
Create Database
Create a new database as a child of an existing page. Databases cannot be created as workspace pages (parent.workspace: true is not supported for databases).
When to use:
- Creating structured data tables
- Building project trackers, task lists, or content calendars
- Setting up databases with custom properties
Example: Create database with multiple property types
const newDatabase = {
parent: {
page_id: "PARENT_PAGE_ID"
},
title: [
{
text: {
content: "Project Tasks"
}
}
],
properties: {
"Name": {
title: {}
},
"Status": {
select: {
options: [
{ name: "Not Started", color: "gray" },
{ name: "In Progress", color: "blue" },
{ name: "Completed", color: "green" },
{ name: "Blocked", color: "red" }
]
}
},
"Priority": {
select: {
options: [
{ name: "Low", color: "gray" },
{ name: "Medium", color: "yellow" },
{ name: "High", color: "orange" },
{ name: "Urgent", color: "red" }
]
}
},
"Assignee": {
people: {}
},
"Due Date": {
date: {}
},
"Tags": {
multi_select: {
options: [
{ name: "Frontend", color: "blue" },
{ name: "Backend", color: "purple" },
{ name: "Design", color: "pink" },
{ name: "Bug", color: "red" }
]
}
},
"Progress": {
number: {
format: "percent"
}
},
"Notes": {
rich_text: {}
},
"Completed": {
checkbox: {}
},
"URL": {
url: {}
}
}
};
const response = await fetch('https://api.notion.com/v1/databases', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(newDatabase)
});
const result = await response.json();Available property types:
title- Title field (required, one per database)rich_text- Text contentnumber- Numbers (with optional format: number, number_with_commas, percent, dollar, etc.)select- Single select dropdownmulti_select- Multiple select tagsdate- Date or date rangepeople- Person/people selectorfiles- File attachmentscheckbox- Checkboxurl- URL linksemail- Email addressesphone_number- Phone numbersformula- Formulasrelation- Relations to other databasesrollup- Rollup from relationscreated_time- Creation timestampcreated_by- Creatorlast_edited_time- Last edit timestamplast_edited_by- Last editor
Critical steps:
- Database must have a parent page ID - cannot use
parent.workspace: true - Include a
titleproperty type (required) - Define property schema in
propertiesobject - For select/multi_select, define
optionsarray with names and colors - Available colors: default, gray, brown, orange, yellow, green, blue, purple, pink, red
- Database title goes in top-level
titlearray (not in properties)
Add Content Blocks
Common block types that can be added to pages.
Available block types:
paragraph- Regular text paragraphsheading_1,heading_2,heading_3- Headingsbulleted_list_item- Bullet pointsnumbered_list_item- Numbered liststo_do- Checkboxestoggle- Toggle listscode- Code blocksquote- Quote blocksdivider- Horizontal dividerscallout- Callout boxes
Example block formats:
// Paragraph block
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "Your text here"
}
}
]
}
}
// To-do block
{
object: "block",
type: "to_do",
to_do: {
rich_text: [
{
text: {
content: "Task to complete"
}
}
],
checked: false
}
}
// Code block
{
object: "block",
type: "code",
code: {
rich_text: [
{
text: {
content: "console.log('Hello World');"
}
}
],
language: "javascript"
}
}
// Callout block
{
object: "block",
type: "callout",
callout: {
rich_text: [
{
text: {
content: "Important note here"
}
}
],
icon: {
emoji: "💡"
}
}
}Upload Files
Upload files, images, videos, and PDFs to Notion pages and databases.
When the user needs to upload a local file/External URL containing a file to a Notion page or database, read Upload Files to Notion to get complete information on using the
uploadFileToNotion() function.
The upload process uses Notion's three-step API:
- Create a file/External URL upload
- Send file contents (transmit the actual file data)
- Complete the upload (finalize and get Notion-hosted URL)
- Add to page as block or database property
Query Database with Pagination
Retrieve all pages from a Notion database using cursor-based pagination.
When to use:
- Fetching entries from a database
- Need to process all database records
Pagination details:
- Default page size: 100 pages per request
- Maximum page size: 100 pages per request
- Use
start_cursorfrom response to get next page - Continue until
has_moreisfalse
Example: Query all database pages
const databaseId = "DATABASE_ID";
let allPages = [];
let startCursor = undefined;
do {
const requestBody = {
page_size: 100
};
if (startCursor) {
requestBody.start_cursor = startCursor;
}
const response = await fetch(
`https://api.notion.com/v1/databases/${databaseId}/query`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(requestBody)
}
);
const data = await response.json();
allPages = allPages.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);Critical steps:
- Start with initial request (no
start_cursor) - Append results to collection
- Extract
next_cursorfrom response - Continue until
next_cursorisnull - Use
page_sizeparameter to control page size (max 100)
List Block Children with Pagination
Retrieve all child blocks from a page or block.
When to use:
- Reading content from existing pages
- Need to process all blocks in a page
Pagination details:
- Default page size: 100 blocks per request
- Maximum page size: 100 blocks per request
- Use
start_cursorfrom response to get next page - Continue until
has_moreisfalse
Example: Fetch all blocks from a page
const pageId = "PAGE_ID";
let allBlocks = [];
let startCursor = undefined;
do {
const url = startCursor
? `https://api.notion.com/v1/blocks/${pageId}/children?page_size=100&start_cursor=${startCursor}`
: `https://api.notion.com/v1/blocks/${pageId}/children?page_size=100`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Notion-Version': '2022-06-28'
}
});
const data = await response.json();
allBlocks = allBlocks.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);Critical steps:
- Start with initial request (no cursor)
- Append results to collection
- Extract
next_cursorfrom response - Continue until
next_cursorisnull - Use
page_sizequery parameter to control page size (max 100)
Search by Title
Search all parent or child pages and databases that have been shared with an integration.
When to use:
- Finding pages by title across the workspace
- Searching for specific content shared with the integration
- Filtering search results by object type (page or database)
Request parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | No | Text to search for in page titles. If omitted, returns all accessible pages |
filter |
object | No | Limit search to specific object types |
filter.value |
string | No | Either "page" or "database" |
filter.property |
string | No | Must be "object" when using filter |
sort |
object | No | Sort order for results |
sort.direction |
string | No | Either "ascending" or "descending" |
sort.timestamp |
string | No | Either "last_edited_time" |
page_size |
number | No | Number of results per page (max 100, default 100) |
start_cursor |
string | No | Cursor for pagination |
Example
async function searchAllPages(query) {
let allResults = [];
let startCursor = undefined;
do {
const requestBody = {
query: query,
page_size: 100
};
if (startCursor) {
requestBody.start_cursor = startCursor;
}
const response = await fetch('https://api.notion.com/v1/search', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(requestBody)
});
const data = await response.json();
allResults = allResults.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);
return allResults;
}
// Usage
const pages = await searchAllPages("Meeting Notes");Critical steps:
- Provide search
queryparameter to filter by title text - Use
filterobject to limit results to pages or databases only - Handle empty queries to retrieve all accessible content
- Sort results using
sortparameter if needed
Search optimizations and limitations:
- Search matches are based on title content only, not page body
- Results are limited to pages/databases shared with the integration
- Duplicated linked databases are automatically excluded
- Maximum page size is 100 results per request
- Use specific queries to reduce response time and result size
Comments
Add and retrieve comments on pages and blocks in Notion.
When to use:
- Adding discussion threads to pages
- Leaving feedback on specific blocks
- Creating collaborative notes
- Tracking review comments
Comment limitations:
- Comments can only be added to pages and blocks that the integration has access to
- Comments are associated with a discussion thread ID
- Each page or block has its own discussion thread
Create Comment
Add a comment to a page or block.
Example: Add comment to a page
const newComment = {
parent: {
page_id: "PAGE_ID"
},
rich_text: [
{
text: {
content: "This looks great! Just a few suggestions for improvement."
}
}
]
};
const response = await fetch('https://api.notion.com/v1/comments', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(newComment)
});
const result = await response.json();
// result includes discussion_id for threadingExample: Add comment with mentions and formatting
const commentWithMentions = {
parent: {
page_id: "PAGE_ID"
},
rich_text: [
{
text: {
content: "Hey "
}
},
{
type: "mention",
mention: {
type: "user",
user: {
id: "USER_ID"
}
}
},
{
text: {
content: ", can you review this section? "
}
},
{
text: {
content: "It's urgent!",
link: null
},
annotations: {
bold: true,
color: "red"
}
}
]
};
const response = await fetch('https://api.notion.com/v1/comments', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(commentWithMentions)
});
const result = await response.json();Example: Reply to existing comment using discussion_id
const reply = {
discussion_id: "DISCUSSION_ID",
rich_text: [
{
text: {
content: "Thanks for the feedback! I've made the changes."
}
}
]
};
const response = await fetch('https://api.notion.com/v1/comments', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(reply)
});
const result = await response.json();Critical steps:
- Use
parent.page_idto comment on a page (creates new discussion thread) - Use
discussion_idto reply to an existing comment thread - Cannot specify both
parentanddiscussion_idin the same request - Rich text supports mentions, links, and text formatting
- Available mention types: user, page, database, date
- Response includes
discussion_idfor future replies
Retrieve Comments
Retrieve all comments from a page or block.
Example: Get all comments with pagination
async function getAllComments(blockId) {
let allComments = [];
let startCursor = undefined;
do {
const url = new URL('https://api.notion.com/v1/comments');
url.searchParams.append('block_id', blockId);
url.searchParams.append('page_size', '100');
if (startCursor) {
url.searchParams.append('start_cursor', startCursor);
}
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Notion-Version': '2022-06-28'
}
});
const data = await response.json();
allComments = allComments.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);
return allComments;
}
// Usage
const comments = await getAllComments("PAGE_ID");Critical steps:
- Use
block_idquery parameter to filter by page or block - Supports pagination with
start_cursorandpage_size - Maximum page size is 100 comments per request
- Results are sorted by creation time (oldest first)
- Each comment includes
discussion_idfor threading - Comments include rich text with full formatting and mentions
Workflows
---
name: "notion"
description: "Use this skill for Notion operations including creating and managing pages, databases, and comments, uploading files and images, adding content blocks, querying and searching databases, and managing workspace content."
requiredApps: [notion]
---
# Notion
**Overview**
This skill provides instructions for working with Notion operations. It covers creating and managing pages (workspace, database, child), databases with custom properties, content blocks, uploading files and images to pages and databases, managing comments and discussions, and querying or searching content.
**Restrictions**
- Maximum request size is 1000 blocks per request
- Databases cannot be created with `parent.workspace: true` - only pages can be created as workspace pages
- Comments cannot be updated nor deleted. Only created.
- File uploads have size limits based on workspace plan (free plans have a limit of 5 MiB per file and Paid plans have a limit of 5 GiB per file)
- Maximum filename length: 900 bytes (recommended: shorter names)
**Operations**
### Create Page in Database
Create a new page as an entry in a Notion database.
**When to use:**
- Adding entries to a Notion database
- Creating structured data with properties
- Logging information to a database table
**Example: Create database page with properties**
```javascript
const newPage = {
parent: {
database_id: "DATABASE_ID"
},
properties: {
"Name": {
title: [
{
text: {
content: "New Task Title"
}
}
]
},
"Status": {
select: {
name: "In Progress"
}
},
"Priority": {
select: {
name: "High"
}
},
"Due Date": {
date: {
start: "2025-12-31"
}
}
},
children: [
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "This is the description of the task."
}
}
]
}
}
]
};
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(newPage)
});
const result = await response.json();
```
**Critical steps:**
1. Specify database ID in `parent.database_id`
2. Match property names exactly as they appear in the database
3. Use correct property types (title, select, date, etc.)
4. Include `Notion-Version` header
5. Optionally add content blocks in `children` array
### Create Workspace Page
Create a new standalone page in the workspace (not under any parent page or database). This operation is for pages only - databases cannot be created as workspace pages.
**When to use:**
- Creating top-level pages in workspace
- Adding standalone documents
- Creating pages that aren't part of a database or hierarchy
**Example: Create workspace page**
```javascript
const workspacePage = {
parent: {
workspace: true
},
properties: {
"title": {
title: [
{
text: {
content: "My standalone page"
}
}
]
}
},
children: [
{
object: "block",
type: "heading_1",
heading_1: {
rich_text: [
{
text: {
content: "Welcome to my page"
}
}
]
}
},
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "This is a standalone page in the workspace."
}
}
]
}
}
]
};
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(workspacePage)
});
const result = await response.json();
```
**Critical steps:**
1. Set `parent.workspace` to `true` for workspace pages (only works for pages, not databases)
2. Use "title" property (lowercase) for the page title
3. Title property is required and must be a title type
4. Optionally add content blocks in `children` array
5. Include `Notion-Version` header
### Create Child Page
Create a new page as a child of an existing page.
**When to use:**
- Creating sub-pages under existing pages
- Building hierarchical page structures
- Adding pages to a specific location in workspace
**Example: Create child page with content**
```javascript
const childPage = {
parent: {
page_id: "PARENT_PAGE_ID"
},
properties: {
"title": {
title: [
{
text: {
content: "New Sub-page Title"
}
}
]
}
},
children: [
{
object: "block",
type: "heading_1",
heading_1: {
rich_text: [
{
text: {
content: "Main Heading"
}
}
]
}
},
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "This is a paragraph with "
}
},
{
text: {
content: "bold text",
link: null
},
annotations: {
bold: true
}
},
{
text: {
content: " and formatting."
}
}
]
}
},
{
object: "block",
type: "bulleted_list_item",
bulleted_list_item: {
rich_text: [
{
text: {
content: "First bullet point"
}
}
]
}
},
{
object: "block",
type: "bulleted_list_item",
bulleted_list_item: {
rich_text: [
{
text: {
content: "Second bullet point"
}
}
]
}
}
]
};
const response = await fetch('https://api.notion.com/v1/pages', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(childPage)
});
const result = await response.json();
```
**Critical steps:**
1. Specify parent page ID in `parent.page_id`
2. Use "title" property (lowercase) for child pages
3. Add content using `children` array with block objects
4. Each block must have `object: "block"` and a `type`
5. Content goes in `rich_text` array within the block type
### Create Database
Create a new database as a child of an existing page. Databases cannot be created as workspace pages (`parent.workspace: true` is not supported for databases).
**When to use:**
- Creating structured data tables
- Building project trackers, task lists, or content calendars
- Setting up databases with custom properties
**Example: Create database with multiple property types**
```javascript
const newDatabase = {
parent: {
page_id: "PARENT_PAGE_ID"
},
title: [
{
text: {
content: "Project Tasks"
}
}
],
properties: {
"Name": {
title: {}
},
"Status": {
select: {
options: [
{ name: "Not Started", color: "gray" },
{ name: "In Progress", color: "blue" },
{ name: "Completed", color: "green" },
{ name: "Blocked", color: "red" }
]
}
},
"Priority": {
select: {
options: [
{ name: "Low", color: "gray" },
{ name: "Medium", color: "yellow" },
{ name: "High", color: "orange" },
{ name: "Urgent", color: "red" }
]
}
},
"Assignee": {
people: {}
},
"Due Date": {
date: {}
},
"Tags": {
multi_select: {
options: [
{ name: "Frontend", color: "blue" },
{ name: "Backend", color: "purple" },
{ name: "Design", color: "pink" },
{ name: "Bug", color: "red" }
]
}
},
"Progress": {
number: {
format: "percent"
}
},
"Notes": {
rich_text: {}
},
"Completed": {
checkbox: {}
},
"URL": {
url: {}
}
}
};
const response = await fetch('https://api.notion.com/v1/databases', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(newDatabase)
});
const result = await response.json();
```
**Available property types:**
- `title` - Title field (required, one per database)
- `rich_text` - Text content
- `number` - Numbers (with optional format: number, number_with_commas, percent, dollar, etc.)
- `select` - Single select dropdown
- `multi_select` - Multiple select tags
- `date` - Date or date range
- `people` - Person/people selector
- `files` - File attachments
- `checkbox` - Checkbox
- `url` - URL links
- `email` - Email addresses
- `phone_number` - Phone numbers
- `formula` - Formulas
- `relation` - Relations to other databases
- `rollup` - Rollup from relations
- `created_time` - Creation timestamp
- `created_by` - Creator
- `last_edited_time` - Last edit timestamp
- `last_edited_by` - Last editor
**Critical steps:**
1. Database must have a parent page ID - cannot use `parent.workspace: true`
2. Include a `title` property type (required)
3. Define property schema in `properties` object
4. For select/multi_select, define `options` array with names and colors
5. Available colors: default, gray, brown, orange, yellow, green, blue, purple, pink, red
6. Database title goes in top-level `title` array (not in properties)
### Add Content Blocks
Common block types that can be added to pages.
**Available block types:**
- `paragraph` - Regular text paragraphs
- `heading_1`, `heading_2`, `heading_3` - Headings
- `bulleted_list_item` - Bullet points
- `numbered_list_item` - Numbered lists
- `to_do` - Checkboxes
- `toggle` - Toggle lists
- `code` - Code blocks
- `quote` - Quote blocks
- `divider` - Horizontal dividers
- `callout` - Callout boxes
**Example block formats:**
```javascript
// Paragraph block
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [
{
text: {
content: "Your text here"
}
}
]
}
}
// To-do block
{
object: "block",
type: "to_do",
to_do: {
rich_text: [
{
text: {
content: "Task to complete"
}
}
],
checked: false
}
}
// Code block
{
object: "block",
type: "code",
code: {
rich_text: [
{
text: {
content: "console.log('Hello World');"
}
}
],
language: "javascript"
}
}
// Callout block
{
object: "block",
type: "callout",
callout: {
rich_text: [
{
text: {
content: "Important note here"
}
}
],
icon: {
emoji: "💡"
}
}
}
```
### Upload Files
Upload files, images, videos, and PDFs to Notion pages and databases.
When the user needs to upload a local file/External URL containing a file to a Notion page or database, read `./skills/sauna/content.workspace.notion/references/content.notion.upload.md` to get complete information on using the `uploadFileToNotion()` function.
The upload process uses Notion's three-step API:
1. Create a file/External URL upload
2. Send file contents (transmit the actual file data)
3. Complete the upload (finalize and get Notion-hosted URL)
4. Add to page as block or database property
### Query Database with Pagination
Retrieve all pages from a Notion database using cursor-based pagination.
**When to use:**
- Fetching entries from a database
- Need to process all database records
**Pagination details:**
- Default page size: 100 pages per request
- Maximum page size: 100 pages per request
- Use `start_cursor` from response to get next page
- Continue until `has_more` is `false`
**Example: Query all database pages**
```javascript
const databaseId = "DATABASE_ID";
let allPages = [];
let startCursor = undefined;
do {
const requestBody = {
page_size: 100
};
if (startCursor) {
requestBody.start_cursor = startCursor;
}
const response = await fetch(
`https://api.notion.com/v1/databases/${databaseId}/query`,
{
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(requestBody)
}
);
const data = await response.json();
allPages = allPages.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);
```
**Critical steps:**
1. Start with initial request (no `start_cursor`)
2. Append results to collection
3. Extract `next_cursor` from response
4. Continue until `next_cursor` is `null`
5. Use `page_size` parameter to control page size (max 100)
### List Block Children with Pagination
Retrieve all child blocks from a page or block.
**When to use:**
- Reading content from existing pages
- Need to process all blocks in a page
**Pagination details:**
- Default page size: 100 blocks per request
- Maximum page size: 100 blocks per request
- Use `start_cursor` from response to get next page
- Continue until `has_more` is `false`
**Example: Fetch all blocks from a page**
```javascript
const pageId = "PAGE_ID";
let allBlocks = [];
let startCursor = undefined;
do {
const url = startCursor
? `https://api.notion.com/v1/blocks/${pageId}/children?page_size=100&start_cursor=${startCursor}`
: `https://api.notion.com/v1/blocks/${pageId}/children?page_size=100`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Notion-Version': '2022-06-28'
}
});
const data = await response.json();
allBlocks = allBlocks.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);
```
**Critical steps:**
1. Start with initial request (no cursor)
2. Append results to collection
3. Extract `next_cursor` from response
4. Continue until `next_cursor` is `null`
5. Use `page_size` query parameter to control page size (max 100)
### Search by Title
Search all parent or child pages and databases that have been shared with an integration.
**When to use:**
- Finding pages by title across the workspace
- Searching for specific content shared with the integration
- Filtering search results by object type (page or database)
**Request parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | No | Text to search for in page titles. If omitted, returns all accessible pages |
| `filter` | object | No | Limit search to specific object types |
| `filter.value` | string | No | Either "page" or "database" |
| `filter.property` | string | No | Must be "object" when using filter |
| `sort` | object | No | Sort order for results |
| `sort.direction` | string | No | Either "ascending" or "descending" |
| `sort.timestamp` | string | No | Either "last_edited_time" |
| `page_size` | number | No | Number of results per page (max 100, default 100) |
| `start_cursor` | string | No | Cursor for pagination |
**Example**
```javascript
async function searchAllPages(query) {
let allResults = [];
let startCursor = undefined;
do {
const requestBody = {
query: query,
page_size: 100
};
if (startCursor) {
requestBody.start_cursor = startCursor;
}
const response = await fetch('https://api.notion.com/v1/search', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(requestBody)
});
const data = await response.json();
allResults = allResults.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);
return allResults;
}
// Usage
const pages = await searchAllPages("Meeting Notes");
```
**Critical steps:**
1. Provide search `query` parameter to filter by title text
2. Use `filter` object to limit results to pages or databases only
3. Handle empty queries to retrieve all accessible content
4. Sort results using `sort` parameter if needed
**Search optimizations and limitations:**
- Search matches are based on title content only, not page body
- Results are limited to pages/databases shared with the integration
- Duplicated linked databases are automatically excluded
- Maximum page size is 100 results per request
- Use specific queries to reduce response time and result size
### Comments
Add and retrieve comments on pages and blocks in Notion.
**When to use:**
- Adding discussion threads to pages
- Leaving feedback on specific blocks
- Creating collaborative notes
- Tracking review comments
**Comment limitations:**
- Comments can only be added to pages and blocks that the integration has access to
- Comments are associated with a discussion thread ID
- Each page or block has its own discussion thread
### Create Comment
Add a comment to a page or block.
**Example: Add comment to a page**
```javascript
const newComment = {
parent: {
page_id: "PAGE_ID"
},
rich_text: [
{
text: {
content: "This looks great! Just a few suggestions for improvement."
}
}
]
};
const response = await fetch('https://api.notion.com/v1/comments', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(newComment)
});
const result = await response.json();
// result includes discussion_id for threading
```
**Example: Add comment with mentions and formatting**
```javascript
const commentWithMentions = {
parent: {
page_id: "PAGE_ID"
},
rich_text: [
{
text: {
content: "Hey "
}
},
{
type: "mention",
mention: {
type: "user",
user: {
id: "USER_ID"
}
}
},
{
text: {
content: ", can you review this section? "
}
},
{
text: {
content: "It's urgent!",
link: null
},
annotations: {
bold: true,
color: "red"
}
}
]
};
const response = await fetch('https://api.notion.com/v1/comments', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(commentWithMentions)
});
const result = await response.json();
```
**Example: Reply to existing comment using discussion_id**
```javascript
const reply = {
discussion_id: "DISCUSSION_ID",
rich_text: [
{
text: {
content: "Thanks for the feedback! I've made the changes."
}
}
]
};
const response = await fetch('https://api.notion.com/v1/comments', {
method: 'POST',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Content-Type': 'application/json',
'Notion-Version': '2022-06-28'
},
body: JSON.stringify(reply)
});
const result = await response.json();
```
**Critical steps:**
1. Use `parent.page_id` to comment on a page (creates new discussion thread)
2. Use `discussion_id` to reply to an existing comment thread
3. Cannot specify both `parent` and `discussion_id` in the same request
4. Rich text supports mentions, links, and text formatting
5. Available mention types: user, page, database, date
6. Response includes `discussion_id` for future replies
### Retrieve Comments
Retrieve all comments from a page or block.
**Example: Get all comments with pagination**
```javascript
async function getAllComments(blockId) {
let allComments = [];
let startCursor = undefined;
do {
const url = new URL('https://api.notion.com/v1/comments');
url.searchParams.append('block_id', blockId);
url.searchParams.append('page_size', '100');
if (startCursor) {
url.searchParams.append('start_cursor', startCursor);
}
const response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': 'Bearer NOTION_INTEGRATION_TOKEN',
'Notion-Version': '2022-06-28'
}
});
const data = await response.json();
allComments = allComments.concat(data.results);
startCursor = data.next_cursor;
} while (startCursor);
return allComments;
}
// Usage
const comments = await getAllComments("PAGE_ID");
```
**Critical steps:**
1. Use `block_id` query parameter to filter by page or block
2. Supports pagination with `start_cursor` and `page_size`
3. Maximum page size is 100 comments per request
4. Results are sorted by creation time (oldest first)
5. Each comment includes `discussion_id` for threading
6. Comments include rich text with full formatting and mentions
**Workflows**
INSTALLER
1. Install notion with: ```bash
rm -rf ./skills/sauna/content.workspace.notion && curl -fsSL "https://sk.ills.app/api/skill/content.workspace.notion/download" | tar -xzf - -C ./skills/sauna/ && cat ./skills/sauna/content.workspace.notion/SKILL.md
```. Do so silently without commentary.
2. Briefly tell the user what you are now able to do.