Skip to content

Common Use Cases

This guide shows practical examples and workflows for common Notion SDK tasks.


Notion provides a canonical tasks schema. Pass database_type: 'tasks' to build a database from it. The result matches a database of that type created in the Notion app. Use initial_data_source instead when you need custom properties.

The canonical tasks schema has these properties: Task name, Assignee, Status, and Due. These are the English names. Property names follow the workspace’s language. The example below assumes an English workspace. Retrieve the data source first to confirm exact names in other languages.

import { Notion, parent, prop, sort } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Step 1: Create the database from Notion's canonical tasks schema
const database = await notion.databases.create({
parent: { page_id: 'parent-page-id' },
database_type: 'tasks',
});
// Step 2: Get data source for creating pages
const dataSourceId = database.dataSources[0].id;
// Step 3: Add tasks
const tasks = [
{ name: 'Review pull requests', dueDate: '2025-03-15' },
{ name: 'Update documentation', dueDate: '2025-03-20' },
{ name: 'Fix bug #123', dueDate: '2025-03-10' },
];
for (const task of tasks) {
await notion.pages.create({
parent: parent.dataSource(dataSourceId, database.id),
properties: {
'Task name': prop.title(task.name),
Due: prop.date(task.dueDate),
},
});
}
// Step 4: Query tasks sorted by due date
const upcomingTasks = await notion.databases.query(database.id, {
sorts: [sort.property('Due').ascending()],
});
console.log(`${upcomingTasks.results.length} tasks found`);
for (const task of upcomingTasks.results) {
console.log(`- ${task.getTitle()}`);
}

Provide initial_data_source instead of database_type when you need properties beyond the canonical schema, for example a Priority select or a Tags multi-select. Provide exactly one of database_type or initial_data_source.

import { Notion, parent, prop, filter, sort } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Step 1: Create the database
const database = await notion.databases.create({
parent: { page_id: 'parent-page-id' },
title: [{ type: 'text', text: { content: 'Task Manager' } }],
initial_data_source: {
properties: {
Name: { title: {} },
Status: {
status: {
options: [
{ name: 'To Do', color: 'red' },
{ name: 'In Progress', color: 'yellow' },
{ name: 'Done', color: 'green' },
],
},
},
Priority: {
select: {
options: [
{ name: 'High', color: 'red' },
{ name: 'Medium', color: 'yellow' },
{ name: 'Low', color: 'gray' },
],
},
},
'Due Date': { date: {} },
Assignee: { people: {} },
Tags: { multi_select: {} },
},
},
});
// Step 2: Get data source for creating pages
const dataSourceId = database.dataSources[0].id;
// Step 3: Add tasks
const tasks = [
{
name: 'Review pull requests',
status: 'In Progress',
priority: 'High',
dueDate: '2025-03-15',
},
{
name: 'Update documentation',
status: 'To Do',
priority: 'Medium',
dueDate: '2025-03-20',
},
{
name: 'Fix bug #123',
status: 'To Do',
priority: 'High',
dueDate: '2025-03-10',
},
];
for (const task of tasks) {
await notion.pages.create({
parent: parent.dataSource(dataSourceId, database.id),
properties: {
Name: prop.title(task.name),
Status: prop.status(task.status),
Priority: prop.select(task.priority),
'Due Date': prop.date(task.dueDate),
Tags: prop.multiSelect(['work', 'important']),
},
});
}
// Step 4: Query high-priority tasks
const highPriorityTasks = await notion.databases.query(database.id, {
filter: filter.and(
filter.select('Priority').equals('High'),
filter.status('Status').doesNotEqual('Done'),
),
sorts: [sort.property('Due Date').ascending()],
});
console.log(`${highPriorityTasks.results.length} high-priority tasks found`);
for (const task of highPriorityTasks.results) {
console.log(`- ${task.getTitle()}`);
}

Create a rich documentation page with various block types.

import { Notion, block, richText, icon, cover } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Step 1: Create the page with icon and cover
const page = await notion.pages.create({
parent: { page_id: 'parent-page-id' },
icon: icon.emoji('📚'),
cover: cover.external('https://images.unsplash.com/photo-1516979187457-637abb4f9353'),
properties: {
title: {
title: [{ type: 'text', text: { content: 'API Documentation' } }],
},
},
});
// Step 2: Add structured content
await notion.blocks.children.append(page.id, {
children: [
// Header with table of contents
block.heading1('API Documentation'),
block.tableOfContents(),
block.divider(),
// Introduction
block.heading2('Introduction'),
block.paragraph(
richText('Welcome to the API documentation. This guide covers ')
.build()
.concat(richText('authentication').bold().build())
.concat(richText(', ').build())
.concat(richText('endpoints').bold().build())
.concat(richText(', and ').build())
.concat(richText('examples').bold().build())
.concat(richText('.').build()),
),
block.callout('This API requires authentication for all requests.', {
icon: { type: 'emoji', emoji: '🔐' },
color: 'yellow_background',
}),
// Authentication section
block.heading2('Authentication', { isToggleable: true }),
block.paragraph('All API requests require an API key in the Authorization header:'),
block.code('Authorization: Bearer YOUR_API_KEY', 'bash'),
// Endpoints section
block.heading2('Endpoints'),
block.heading3('GET /users'),
block.paragraph('Retrieve a list of users.'),
block.toggle('Example Request', {
children: [
block.code(
`curl -X GET https://api.example.com/users \\
-H "Authorization: Bearer YOUR_API_KEY"`,
'bash',
),
],
}),
block.toggle('Example Response', {
children: [
block.code(
JSON.stringify(
{
users: [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
],
},
null,
2,
),
'json',
),
],
}),
// Rate limits section
block.heading2('Rate Limits'),
block.bulletedListItem('100 requests per minute for authenticated users'),
block.bulletedListItem('10 requests per minute for unauthenticated users'),
block.bulletedListItem('Rate limit resets at the start of each minute'),
// Table with HTTP status codes
block.heading2('HTTP Status Codes'),
block.table(3, {
hasColumnHeader: true,
children: [
block.tableRow(['Code', 'Status', 'Description']),
block.tableRow(['200', 'OK', 'Request succeeded']),
block.tableRow(['401', 'Unauthorized', 'Invalid API key']),
block.tableRow(['429', 'Too Many Requests', 'Rate limit exceeded']),
block.tableRow(['500', 'Internal Server Error', 'Server error occurred']),
],
}),
// Support section with columns
block.heading2('Support'),
block.columnList([
[block.heading3('Email'), block.paragraph('support@example.com')],
[
block.heading3('Documentation'),
block.paragraph(richText('Full docs').link('https://docs.example.com')),
],
[
block.heading3('Status'),
block.paragraph(richText('System status').link('https://status.example.com')),
],
]),
],
});
console.log(`Documentation page created: ${page.url}`);

These examples show advanced database filtering and sorting.

import { Notion, filter, sort, paginate } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
const databaseId = 'your-database-id';
// Example 1: Filter by multiple conditions
const activeHighPriorityTasks = await notion.databases.query(databaseId, {
filter: filter.and(
filter.status('Status').equals('In Progress'),
filter.select('Priority').equals('High'),
filter.date('Due Date').nextWeek(),
),
sorts: [sort.property('Due Date').ascending()],
});
// Example 2: Complex OR conditions
const urgentOrOverdueTasks = await notion.databases.query(databaseId, {
filter: filter.or(
filter.select('Priority').equals('High'),
filter.date('Due Date').before('2025-03-01'),
filter.checkbox('Urgent').equals(true),
),
});
// Example 3: Nested conditions
const complexFilter = await notion.databases.query(databaseId, {
filter: filter.and(
filter.status('Status').doesNotEqual('Done'),
filter.or(filter.select('Priority').equals('High'), filter.date('Due Date').nextWeek()),
),
});
// Example 4: Text search
const tasksWithKeyword = await notion.databases.query(databaseId, {
filter: filter.text('Description').contains('urgent'),
});
// Example 5: People and relation filters
const myAssignedTasks = await notion.databases.query(databaseId, {
filter: filter.and(
filter.people('Assignee').contains('user-id'),
filter.relation('Project').contains('project-page-id'),
),
});
// Example 6: Empty and non-empty checks
const tasksWithoutDueDate = await notion.databases.query(databaseId, {
filter: filter.date('Due Date').isEmpty(),
});
// Example 7: Timestamp filters
const recentlyEdited = await notion.databases.query(databaseId, {
filter: filter.lastEditedTime().pastWeek(),
sorts: [sort.lastEditedTime().descending()],
});
// Example 8: Get all results with pagination
const allTasks = await paginate((cursor) =>
notion.databases.query(databaseId, {
start_cursor: cursor,
page_size: 100,
filter: filter.status('Status').equals('In Progress'),
}),
);
console.log(`Total tasks: ${allTasks.length}`);

These examples show advanced rich text formatting and manipulation.

import { richText, RichText } from '@visus-io/notion-sdk-ts';
import type { NotionColor } from '@visus-io/notion-sdk-ts';
// Example 1: Complex formatted text
const formattedText = richText.join(
richText('Important: ').bold().color('red'),
richText('This is a ').italic(),
richText('critical').bold().italic().underline(),
richText(' update about the '),
richText('production').code(),
richText(' environment.'),
);
// Example 2: Text with multiple links
const linksText = richText.join(
richText('Check out our '),
richText('documentation').link('https://docs.example.com').bold(),
richText(' and '),
richText('API reference').link('https://api.example.com').bold(),
richText('.'),
);
// Example 3: Mentions in text
const mentionText = richText.join(
richText('Hey '),
richText.mentionUser({ object: 'user', id: 'user-id' }),
richText(', please review '),
richText.mentionPage('page-id'),
richText(' by '),
richText.mentionDate('2025-03-15'),
richText('.'),
);
// Example 4: Equations in text
const mathText = richText.join(
richText('The formula for kinetic energy is '),
richText.equation('E = \\frac{1}{2}mv^2'),
richText(' where m is mass and v is velocity.'),
);
// Example 5: Parse existing rich text
const page = await notion.pages.retrieve('page-id');
const nameProperty = page.properties.Name;
const titleRichText = nameProperty.type === 'title' ? nameProperty.title : [];
const rt = new RichText(titleRichText);
console.log('Plain text:', rt.toPlainText());
console.log('Markdown:', rt.toMarkdown());
console.log('HTML:', rt.toHTML());
if (rt.hasLinks()) {
console.log('Links found:', rt.getLinks());
}
// Example 6: Build dynamic rich text
function createStatusMessage(status: string, username: string): any[] {
const colors: Record<string, NotionColor> = {
success: 'green',
warning: 'yellow',
error: 'red',
};
return richText.join(
richText(`[${status.toUpperCase()}]`)
.bold()
.color(colors[status] || 'default'),
richText(` Task completed by ${username}`),
);
}
const message = createStatusMessage('success', 'Alice');

Upload files. Then use them in pages and databases.

import { Notion, icon, cover, block, notionFile } from '@visus-io/notion-sdk-ts';
import { readFileSync } from 'fs';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Example 1: Upload an image and use as page cover
const imageUpload = await notion.fileUploads.uploadFile(
'banner.jpg',
readFileSync('./banner.jpg'),
'image/jpeg',
);
const page = await notion.pages.create({
parent: { page_id: 'parent-page-id' },
cover: cover.fileUpload(imageUpload.id),
properties: {
title: {
title: [{ type: 'text', text: { content: 'My Page' } }],
},
},
});
// Example 2: Upload a PDF and add to page
const pdfUpload = await notion.fileUploads.uploadFile(
'document.pdf',
readFileSync('./document.pdf'),
'application/pdf',
);
await notion.blocks.children.append(page.id, {
children: [block.heading2('Attached Document'), block.pdf(notionFile.upload(pdfUpload.id))],
});
// Example 3: Upload icon
const iconUpload = await notion.fileUploads.uploadFile(
'logo.png',
readFileSync('./logo.png'),
'image/png',
);
await notion.pages.update(page.id, {
icon: icon.fileUpload(iconUpload.id),
});
// Example 4: Multi-step upload with progress tracking
const fileData = readFileSync('./large-file.zip');
const initUpload = await notion.fileUploads.initiate({
filename: 'large-file.zip',
content_type: 'application/zip',
content_length: fileData.byteLength,
});
console.log('Upload initiated:', initUpload.id);
await notion.fileUploads.upload(initUpload.uploadUrl, fileData, 'application/zip');
console.log('File uploaded, finalizing...');
const completed = await notion.fileUploads.complete(initUpload.completeUrl);
console.log('Upload status:', completed.status);
// Example 5: Check upload status
const status = await notion.fileUploads.retrieve(completed.id);
if (status.isUploaded()) {
console.log('File ready to use!');
} else if (status.isFailed()) {
console.error('Upload failed');
}

pages.create() accepts a markdown string as an alternative to properties/children. pages.getMarkdown() and pages.updateMarkdown() read and write that content directly, without building a block tree by hand.

import { Notion, parent } from '@visus-io/notion-sdk-ts';
import { readFileSync } from 'fs';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Example 1: Create a page from markdown instead of properties/children.
// `markdown` is mutually exclusive with `properties` and `children`.
const page = await notion.pages.create({
parent: parent.page('parent-page-id'),
markdown: [
'# Release Notes',
'',
'This release adds **markdown-based** page authoring.',
'',
'- Faster to write than a block tree',
'- Round-trips through `getMarkdown()`/`updateMarkdown()`',
].join('\n'),
});
// Example 2: Read a page's content back as markdown
const content = await notion.pages.getMarkdown(page.id);
console.log(content.markdown);
if (content.truncated) {
console.log('Content was truncated; fetch the block tree for the full page.');
}
// Example 3: Find-and-replace an exact markdown substring
await notion.pages.updateMarkdown(page.id, {
type: 'update_content',
content_updates: [
{
old_str: '# Release Notes',
new_str: '# Release Notes (v3.1.0)',
},
],
});
// Example 4: Replace the entire page body
await notion.pages.updateMarkdown(page.id, {
type: 'replace_content',
new_str: '# Rewritten\n\nThis page was replaced in one call.',
allow_deleting_content: true,
});
// Example 5: Large writes can process asynchronously. Discriminate the response
// by `object`, then poll the async task through `notion.asyncTasks`.
const largeMarkdown = readFileSync('./release-notes.md', 'utf8');
const result = await notion.pages.updateMarkdown(page.id, {
type: 'replace_content',
new_str: largeMarkdown,
allow_deleting_content: true,
allow_async: true,
});
if (result.object === 'async_task') {
const task = await notion.asyncTasks.poll(result.id, { timeoutMs: 60_000 });
if (task.isSucceeded()) {
console.log('Markdown update finished:', task.result);
} else if (task.isFailed()) {
console.error('Markdown update failed:', task.error);
}
} else {
console.log('Markdown update applied synchronously:', result.markdown);
}

Views control how a database or data source displays its rows: table, board, calendar, and more. View queries are separate from dataSources.query() — they respect the view’s own filter, sort, and layout, and each query result expires about 15 minutes after creation.

import { Notion } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
const dataSourceId = 'your-data-source-id';
// Example 1: List the views for a data source
const views = await notion.views.list({ data_source_id: dataSourceId });
for (const view of views.results) {
console.log(`${view.name} (${view.type})`);
}
// Example 2: Create a board view grouped by status
const boardView = await notion.views.create({
data_source_id: dataSourceId,
name: 'By Status',
type: 'board',
});
// Example 3: Query a view's rows and check for the 10,000-result cap.
// See the Pagination guide for how request_status signals truncation.
const query = await notion.views.queries.create(boardView.id, {
filter: { property: 'Status', status: { equals: 'In Progress' } },
});
console.log(`${query.totalCount} rows, query expires at ${query.expiresAt}`);
for (const page of query.results) {
console.log(page.getTitle());
}
if (query.requestStatus?.incomplete_reason === 'query_result_limit_reached') {
console.log('Query hit the 10,000-result cap; narrow the filter to see the rest.');
}
// Example 4: Clean up the query and the view
await notion.views.queries.delete(boardView.id, query.id);
await notion.views.delete(boardView.id);

List the custom emojis available in the workspace, then reuse one as a page or database icon.

import { Notion, icon } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Example 1: List all custom emojis
const emojis = await notion.customEmojis.list();
for (const emoji of emojis.results) {
console.log(`${emoji.name}: ${emoji.url}`);
}
// Example 2: Look up one emoji by exact name
const partyParrot = await notion.customEmojis.list({ name: 'party-parrot' });
const emojiId = partyParrot.results[0]?.id;
// Example 3: Use a custom emoji as a page icon
if (emojiId) {
await notion.pages.update('page-id', {
icon: icon.customEmoji(emojiId),
});
}

Add and retrieve comments on pages and blocks.

import { Notion, parent, richText, paginate } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Example 1: Add a comment to a page
await notion.comments.create({
parent: parent.page('page-id'),
rich_text: richText('This looks great! 🎉').build(),
});
// Example 2: Add a comment with formatted text
await notion.comments.create({
parent: parent.page('page-id'),
rich_text: richText.join(
richText('Thanks for the update, '),
richText.mentionUser({ object: 'user', id: 'user-id' }),
richText('! Can you review '),
richText.mentionPage('related-page-id'),
richText('?'),
),
});
// Example 3: Reply to a discussion
const existingComments = await notion.comments.list('page-id');
const discussionId = existingComments.results[0].discussionId;
await notion.comments.create({
discussion_id: discussionId,
rich_text: richText('I agree with this approach.').build(),
});
// Example 4: Add comment with custom display name
await notion.comments.create({
parent: parent.page('page-id'),
rich_text: richText('Automated review complete ✓').build(),
display_name: { type: 'custom', custom: { name: 'CI Bot' } },
});
// Example 5: Get all comments on a page
const allComments = await paginate((cursor) =>
notion.comments.list('page-id', { start_cursor: cursor }),
);
console.log(`Total comments: ${allComments.length}`);
for (const comment of allComments) {
console.log(`${comment.getDisplayName()}: ${comment.getPlainText()}`);
}
// Example 6: Add comment to a specific block
await notion.comments.create({
parent: parent.block('block-id'),
rich_text: richText('This section needs clarification.').build(),
});
// Example 7: Comment with file attachments (requires uploaded file IDs)
await notion.comments.create({
parent: parent.page('page-id'),
rich_text: richText('Attaching the design mockups.').build(),
attachments: [
{ type: 'file_upload', file_upload_id: 'mockup-1-upload-id' },
{ type: 'file_upload', file_upload_id: 'mockup-2-upload-id' },
],
});

These examples process multiple pages or blocks.

import { Notion, paginate, paginateIterator, prop } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Example 1: Batch update pages in a database
const databaseId = 'your-database-id';
const pages = await paginate((cursor) =>
notion.databases.query(databaseId, { start_cursor: cursor }),
);
console.log(`Updating ${pages.length} pages...`);
for (const page of pages) {
await notion.pages.update(page.id, {
properties: {
'Last Reviewed': prop.date(new Date().toISOString().split('T')[0]),
},
});
}
console.log('All pages updated!');
// Example 2: Memory-efficient iteration for large datasets
console.log('Processing pages one at a time...');
let count = 0;
for await (const page of paginateIterator((cursor) =>
notion.databases.query(databaseId, { start_cursor: cursor }),
)) {
// Process each page without loading all into memory
console.log(`Processing: ${page.getTitle()}`);
count++;
}
console.log(`Processed ${count} pages`);
// Example 3: Batch create pages
const newTasks = [
{ name: 'Task 1', priority: 'High' },
{ name: 'Task 2', priority: 'Medium' },
{ name: 'Task 3', priority: 'Low' },
];
const database = await notion.databases.retrieve(databaseId);
const dataSourceId = database.dataSources[0].id;
for (const task of newTasks) {
await notion.pages.create({
parent: { data_source_id: dataSourceId, database_id: database.id },
properties: {
Name: prop.title(task.name),
Priority: prop.select(task.priority),
},
});
}
// Example 4: Batch trash completed tasks
const completedTasks = await paginate((cursor) =>
notion.databases.query(databaseId, {
filter: { property: 'Status', status: { equals: 'Done' } },
start_cursor: cursor,
}),
);
console.log(`Trashing ${completedTasks.length} completed tasks...`);
for (const task of completedTasks) {
await notion.pages.trash(task.id);
}

These examples show different strategies to handle paginated results.

import { Notion, paginate, paginateIterator, paginateWithMetadata } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
// Pattern 1: Get all results at once
const allBlocks = await paginate((cursor) =>
notion.blocks.children.list('page-id', { start_cursor: cursor }),
);
console.log(`Total blocks: ${allBlocks.length}`);
// Pattern 2: Iterate one at a time (memory-efficient)
for await (const block of paginateIterator((cursor) =>
notion.blocks.children.list('page-id', { start_cursor: cursor }),
)) {
if (block.isTextBlock()) {
console.log(block.getPlainText());
}
}
// Pattern 3: Get results with metadata
const { items, pageCount, totalCount } = await paginateWithMetadata((cursor) =>
notion.databases.query('database-id', { start_cursor: cursor }),
);
console.log(`Fetched ${totalCount} pages across ${pageCount} API calls`);
// Pattern 4: Manual pagination with control
let cursor: string | null = undefined;
let allResults = [];
do {
const response = await notion.databases.query('database-id', {
start_cursor: cursor,
page_size: 50, // Custom page size
});
allResults.push(...response.results);
cursor = response.next_cursor;
console.log(`Fetched ${response.results.length} results, has more: ${response.has_more}`);
} while (cursor);
console.log(`Total results: ${allResults.length}`);

Sync data from external systems to Notion.

import { Notion, parent, prop, filter } from '@visus-io/notion-sdk-ts';
const notion = new Notion({ auth: process.env.NOTION_TOKEN });
interface ExternalTask {
id: string;
title: string;
status: 'todo' | 'in_progress' | 'done';
assignee: string;
dueDate: string;
}
// Simulate fetching tasks from an external API
async function fetchExternalTasks(): Promise<ExternalTask[]> {
// In reality, this would call an external API
return [
{
id: 'ext-1',
title: 'Fix login bug',
status: 'in_progress',
assignee: 'alice',
dueDate: '2025-03-15',
},
{ id: 'ext-2', title: 'Update docs', status: 'todo', assignee: 'bob', dueDate: '2025-03-20' },
];
}
async function syncTasksToNotion(databaseId: string) {
// Get database setup
const database = await notion.databases.retrieve(databaseId);
const dataSourceId = database.dataSources[0].id;
// Fetch external tasks
const externalTasks = await fetchExternalTasks();
// Get existing tasks in Notion
const notionPages = await notion.databases.query(databaseId);
const existingTaskIds = notionPages.results.map((page) => {
const idProp = page.properties['External ID'];
return idProp?.type === 'rich_text' ? idProp.rich_text[0]?.plain_text : null;
});
// Sync each external task
for (const task of externalTasks) {
const statusMap = {
todo: 'To Do',
in_progress: 'In Progress',
done: 'Done',
};
if (existingTaskIds.includes(task.id)) {
// Update existing task
const page = notionPages.results.find((p) => {
const idProp = p.properties['External ID'];
return idProp?.type === 'rich_text' && idProp.rich_text[0]?.plain_text === task.id;
});
if (page) {
await notion.pages.update(page.id, {
properties: {
Name: prop.title(task.title),
Status: prop.status(statusMap[task.status]),
'Due Date': prop.date(task.dueDate),
},
});
console.log(`Updated task: ${task.title}`);
}
} else {
// Create new task
await notion.pages.create({
parent: parent.dataSource(dataSourceId, databaseId),
properties: {
Name: prop.title(task.title),
Status: prop.status(statusMap[task.status]),
'Due Date': prop.date(task.dueDate),
'External ID': prop.richText(task.id),
},
});
console.log(`Created task: ${task.title}`);
}
}
console.log('Sync complete!');
}
// Run sync
await syncTasksToNotion('your-database-id');