### Install and Scaffold Notion Worker CLI Source: https://github.com/makenotion/workers-template/blob/main/README.md Installs the ntn CLI and scaffolds a new worker project. Navigate into the created directory to begin development. ```shell curl -fsSL https://ntn.dev | bash ntn workers new cd my-worker ``` -------------------------------- ### Start OAuth Flow Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Initiates the OAuth flow for a specified OAuth provider. Replace `` with the actual name of your OAuth provider. ```shell ntn workers oauth start ``` -------------------------------- ### Install Notion Workers CLI Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Installs the Notion Workers CLI using a curl script. This is the first step to scaffolding and managing your workers. ```shell curl -fsSL https://ntn.dev | bash ``` -------------------------------- ### Start OAuth Flow Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Initiates the OAuth flow for a configured service, such as GitHub. This is used to obtain user authorization. ```shell ntn workers oauth start githubAuth ``` -------------------------------- ### Simple Replace Sync Example Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.md Use this sync strategy for small data sources or APIs lacking change tracking. It replaces the entire dataset on each scheduled run. ```typescript const records = worker.database("records", { type: "managed", initialTitle: "Records", primaryKeyProperty: "ID", schema: { properties: { Name: Schema.title(), ID: Schema.richText() } }, }); const myApi = worker.pacer("myApi", { allowedRequests: 10, intervalMs: 1000 }); worker.sync("recordsSync", { database: records, mode: "replace", schedule: "1h", execute: async (state) => { const page = state?.page ?? 1; await myApi.wait(); const { items, hasMore } = await fetchPage(page, 100); return { changes: items.map((item) => ({ type: "upsert" as const, key: item.id, properties: { Name: Builder.title(item.name), ID: Builder.richText(item.id) }, })), hasMore, nextState: hasMore ? { page: page + 1 } : undefined, }; }, }); ``` -------------------------------- ### Initialize Notion Worker Source: https://context7.com/makenotion/workers-template/llms.txt Every Notion Worker must start by creating a Worker instance and exporting it as the default export. This instance serves as the foundation for registering all capabilities. ```typescript import { Worker } from "@notionhq/workers"; const worker = new Worker(); export default worker; ``` -------------------------------- ### Paginated Replace Sync Example Source: https://github.com/makenotion/workers-template/blob/main/AGENTS.alpha.md Demonstrates a 'replace' sync strategy that fetches data in pages. Use this when the API lacks change tracking. Ensure to handle pagination correctly by returning `hasMore: true` and `nextState` until all data is fetched. ```typescript worker.sync("paginatedSync", { mode: "replace", primaryKeyProperty: "ID", schema: { defaultName: "Records", properties: { Name: Schema.title(), ID: Schema.richText() } }, execute: async (state, { notion }) => { const page = state?.page ?? 1; const pageSize = 100; const { items, hasMore } = await fetchPage(page, pageSize); return { changes: items.map((item) => ({ type: "upsert", key: item.id, properties: { Name: Builder.title(item.name), ID: Builder.richText(item.id) }, })), hasMore, nextState: hasMore ? { page: page + 1 } : undefined, }; }, }); ``` -------------------------------- ### Run Type Check Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Executes type checking for the project. This command is part of the local development setup. ```shell npm run check ``` -------------------------------- ### Delta Sync Example Source: https://github.com/makenotion/workers-template/blob/main/AGENTS.md The delta sync in a backfill + delta pair strategy. It fetches recent changes frequently to keep data current with minimal API usage. ```typescript const tasks = worker.database("tasks", { type: "managed", initialTitle: "Tasks", primaryKeyProperty: "Task ID", schema: { properties: { "Task Name": Schema.title(), "Task ID": Schema.richText(), Status: Schema.select([{ name: "Open" }, { name: "Done", color: "green" }]), }, }, }); const taskApi = worker.pacer("taskApi", { allowedRequests: 10, intervalMs: 1000 }); // Delta: fetches recent changes, runs every 5 minutes. worker.sync("tasksDelta", { database: tasks, mode: "incremental", schedule: "5m", execute: async (state) => { const cursor = state?.cursor; await taskApi.wait(); const { items, nextCursor } = await fetchTaskChanges(cursor); return { changes: items.map((item) => ({ type: "upsert" as const, key: item.id, properties: { "Task Name": Builder.title(item.name), "Task ID": Builder.richText(item.id), Status: Builder.select(item.status), }, })), hasMore: Boolean(nextCursor), nextState: nextCursor ? { cursor: nextCursor } : undefined, }; }, }); ``` -------------------------------- ### Define a Notion Automation Source: https://context7.com/makenotion/workers-template/llms.txt Use `worker.automation` to define a new automation. This example sends a welcome email and updates a Notion page when a new customer is added. Requires `SENDGRID_API_KEY` environment variable. ```typescript import { Worker } from "@notionhq/workers"; const worker = new Worker(); export default worker; type RichTextProperty = { type: "rich_text"; rich_text: Array<{ plain_text: string }>; }; worker.automation("sendWelcomeEmail", { title: "Send Welcome Email", description: "Sends a welcome email when a new customer is added", execute: async (event, { notion }) => { const { pageId, pageData } = event; const emailProperty = pageData?.properties?.Email as RichTextProperty | undefined; const email = emailProperty?.rich_text.map((rt) => rt.plain_text).join("") ?? ""; if (!email || !pageId) return; // Send email via your email service await fetch("https://api.sendgrid.com/v3/mail/send", { method: "POST", headers: { Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ to: email, from: "welcome@example.com", subject: "Welcome!", text: "Thanks for signing up!", }), }); // Update the page to mark email as sent await notion.pages.update({ page_id: pageId, properties: { EmailSent: { checkbox: true }, }, }); }, }); ``` -------------------------------- ### Execute Worker Capability Locally (TypeScript) Source: https://github.com/makenotion/workers-template/blob/main/AGENTS.alpha.md Example TypeScript script to execute a worker capability locally. It uses `execSync` to run the command and logs the output. ```typescript import { execSync } from "child_process"; function exec(capability: string, input: Record) { const result = execSync( `ntn workers exec ${capability} --local -d '${JSON.stringify(input)}'`, { encoding: "utf-8" }, ); console.log(result); } exec("sayHello", { name: "World" }); ``` -------------------------------- ### Get Logs for Latest Run Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Retrieve logs for the latest run of any capability or a specific capability. The `--plain` flag outputs tab-separated values for easy piping. ```shell ntn workers runs list --plain | head -n1 | cut -f1 | xargs -I{} ntn workers runs logs {} ``` ```shell ntn workers runs list --plain | grep tasksSync | head -n1 | cut -f1 | xargs -I{} ntn workers runs logs {} ``` -------------------------------- ### TypeScript Test Script for Worker Execution Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Example TypeScript script to test worker capabilities. It uses `child_process.execSync` to execute commands locally and logs the output. ```typescript import { execSync } from "child_process"; function exec(capability: string, input: Record) { const result = execSync( `ntn workers exec ${capability} --local -d '${JSON.stringify(input)}'`, { encoding: "utf-8" }, ); console.log(result); } exec("sayHello", { name: "World" }); ``` -------------------------------- ### Bash Test Script for Worker Execution Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Example bash script to test worker capabilities. It demonstrates both local execution using `.env` and remote execution against a deployed worker. ```shell #!/usr/bin/env bash set -euo pipefail # Run locally (uses .env automatically): ntn workers exec sayHello --local -d '{"name": "World"}' # Or run against the deployed worker (requires `ntn workers deploy` and `ntn workers env push` first): # ntn workers exec sayHello -d '{"name": "World"}' ``` -------------------------------- ### Declare a Managed Database Source: https://context7.com/makenotion/workers-template/llms.txt Databases are declared separately and referenced by handle. Define the schema using `Schema` helpers and set a `primaryKeyProperty` for unique record identification. This example declares a 'customers' database with various property types. ```typescript import { Worker } from "@notionhq/workers"; import * as Schema from "@notionhq/workers/schema"; const worker = new Worker(); export default worker; const customers = worker.database("customers", { type: "managed", initialTitle: "Customers", primaryKeyProperty: "Customer ID", schema: { properties: { Name: Schema.title(), "Customer ID": Schema.richText(), Email: Schema.email(), "Signup Date": Schema.date(), Plan: Schema.select([ { name: "Free", color: "gray" }, { name: "Pro", color: "blue" }, { name: "Enterprise", color: "purple" }, ]), "Monthly Revenue": Schema.number("dollar"), Active: Schema.checkbox(), }, }, }); ``` -------------------------------- ### Define Worker, Database, Pacer, Sync, Tool, and OAuth Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.md This snippet demonstrates the core setup for a Notion worker, including defining a managed database, a pacer for rate limiting API calls, a sync to populate the database, a tool for user interaction, and an OAuth capability for external authentication. ```typescript import { Worker } from "@notionhq/workers"; import * as Builder from "@notionhq/workers/builder"; import * as Schema from "@notionhq/workers/schema"; const worker = new Worker(); export default worker; // Declare a database const tasks = worker.database("tasks", { type: "managed", initialTitle: "Tasks", primaryKeyProperty: "ID", schema: { properties: { Name: Schema.title(), ID: Schema.richText() } }, }); // Declare a pacer for the upstream API const myApi = worker.pacer("myApi", { allowedRequests: 10, intervalMs: 1000 }); // Declare a sync that writes to the database worker.sync("tasksSync", { database: tasks, execute: async (state) => { await myApi.wait(); const items = await fetchItems(state?.page ?? 1); return { changes: items.map((i) => ({ type: "upsert" as const, key: i.id, properties: { Name: Builder.title(i.name), ID: Builder.richText(i.id) }, })), hasMore: false, }; }, }); worker.tool("sayHello", { title: "Say Hello", description: "Return a greeting", schema: { type: "object", properties: { name: { type: "string" } }, required: ["name"], additionalProperties: false }, execute: ({ name }, { notion }) => `Hello, ${name}`, }); worker.oauth("googleAuth", { name: "my-google-auth", provider: "google" }); ``` -------------------------------- ### Two-Way Relation Sync Example Source: https://github.com/makenotion/workers-template/blob/main/AGENTS.alpha.md Shows how to define a two-way relation between two syncs, automatically creating reciprocal properties. This simplifies maintaining consistency between related data, such as projects and tasks. ```typescript worker.sync("projectsSync", { primaryKeyProperty: "Project ID", ... }); // Example sync worker that syncs sample tasks to a database worker.sync("tasksSync", { primaryKeyProperty: "Task ID", ... schema: { ... properties: { ... Project: Schema.relation("projectsSync", { // Optionally configure a two-way relation. This will automatically create the // "Tasks" property on the project synced database: there is no need // to configure "Tasks" on the projectSync capability. twoWay: true, relatedPropertyName: "Tasks" }), }, }, execute: async () => { // Return sample tasks as database entries const tasks = fetchTasks() const changes = tasks.map((task) => ({ type: "upsert" as const, key: task.id, properties: { ... Project: [Builder.relation(task.projectId)], }, })); return { changes, hasMore: false, }; }, }); ``` -------------------------------- ### Incremental Sync with Deletes Example Source: https://github.com/makenotion/workers-template/blob/main/AGENTS.alpha.md Illustrates an 'incremental' sync strategy that fetches only changes since the last run and handles deletions. This mode is suitable for APIs with change tracking. Explicitly emit delete markers for records that have been removed. ```typescript worker.sync("incrementalSync", { primaryKeyProperty: "ID", mode: "incremental", schema: { defaultName: "Records", properties: { Name: Schema.title(), ID: Schema.richText() } }, execute: async (state, { notion }) => { const { upserts, deletes, nextCursor } = await fetchChanges(state?.cursor); return { changes: [ ...upserts.map((item) => ({ type: "upsert", key: item.id, properties: { Name: Builder.title(item.name), ID: Builder.richText(item.id) }, })), ...deletes.map((id) => ({ type: "delete", key: id })), ], hasMore: Boolean(nextCursor), nextState: nextCursor ? { cursor: nextCursor } : undefined, }; }, }); ``` -------------------------------- ### Defining Relations Between Syncs Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Establish relationships between sync workers using `Schema.relation` and `Builder.relation`. This example shows how tasks can be related to projects, with an option for a two-way relation that automatically configures the inverse property. ```typescript worker.sync("projectsSync", { primaryKeyProperty: "Project ID", // ... other configurations }); // Example sync worker that syncs sample tasks to a database worker.sync("tasksSync", { primaryKeyProperty: "Task ID", // ... other configurations schema: { // ... other schema properties properties: { // ... other properties Project: Schema.relation("projectsSync", { // Optionally configure a two-way relation. This will automatically create the // "Tasks" property on the project synced database: there is no need // to configure "Tasks" on the projectSync capability. twoWay: true, relatedPropertyName: "Tasks" }), }, }, execute: async () => { // Return sample tasks as database entries const tasks = fetchTasks() const changes = tasks.map((task) => ({ type: "upsert" as const, key: task.id, properties: { // ... other properties Project: [Builder.relation(task.projectId)], }, })); return { changes, hasMore: false, }; }, }); ``` -------------------------------- ### Display Help Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Shows help information for all available CLI commands. ```shell ntn --help ``` -------------------------------- ### Preview Sync Output Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Trigger a sync and preview its output without making any changes to the database. Replace `` with the actual sync key. ```shell ntn workers sync trigger --preview ``` -------------------------------- ### Get Logs for a Specific Run Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Retrieve logs for a specific worker run using its run ID. ```shell ntn workers runs logs ``` -------------------------------- ### Manage Sync Capabilities Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md List, disable, or enable sync capabilities using the CLI. ```shell ntn workers capabilities list ``` ```shell ntn workers capabilities disable ``` ```shell ntn workers capabilities enable ``` -------------------------------- ### Build and Deploy Workers Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Commands for building, checking types, logging in, deploying, and executing workers. ```shell npm run build ``` ```shell npm run check ``` ```shell ntn login ``` ```shell ntn workers deploy ``` ```shell ntn workers exec ``` ```shell ntn workers sync status ``` ```shell ntn workers sync trigger --preview ``` ```shell ntn workers sync trigger ``` -------------------------------- ### View Status for Specific Sync Source: https://context7.com/makenotion/workers-template/llms.txt Get the status for a particular sync task using `ntn workers sync status `. ```shell ntn workers sync status tasksSync ``` -------------------------------- ### Build Project Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Emits the build output to the `dist/` directory. This command is used during local development. ```shell npm run build ``` -------------------------------- ### Enable a Sync Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Command to enable a specific sync capability. ```shell ntn workers capabilities enable ``` -------------------------------- ### Preview Sync Output CLI Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.md Use 'ntn workers sync trigger --preview' to inspect the data your sync execute function would produce without writing to the Notion database. You can also resume a previous preview using the --context flag. ```shell ntn workers sync trigger --preview # run execute, show objects, don't write to the database ntn workers sync trigger --preview --context '{"page":2}' # resume from a previous preview's nextContext ``` -------------------------------- ### Login to CLI Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Command to log in to the CLI. ```shell ntn login ``` -------------------------------- ### Makenotion CLI Commands Source: https://github.com/makenotion/workers-template/blob/main/README.md Provides essential command-line interface commands for deploying, testing, monitoring, and managing Makenotion workers and syncs. ```shell # Deploy your worker to Notion ntn workers deploy ``` ```shell # Test a tool locally ntn workers exec --local -d '{"key": "value"}' ``` ```shell # Monitor sync status (live-updating) ntn workers sync status ``` ```shell # Preview sync output without writing to the database ntn workers sync trigger --preview ``` ```shell # Trigger a real sync immediately (writes to the database, bypasses schedule) ntn workers sync trigger ``` ```shell # Reset sync state (restart from scratch) ntn workers sync state reset ``` ```shell # List all capabilities ntn workers capabilities list ``` -------------------------------- ### Create Podcast from Notion Page with ElevenLabs Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Convert Notion page content to audio using the ElevenLabs API. Requires an ElevenLabs API key and a voice ID. The `content` and `voiceId` are provided as parameters. ```typescript worker.tool("createPodcast", { title: "Create Podcast from Page", description: "Convert page content to audio using ElevenLabs", schema: j.object({ content: j.string().describe("Page content to convert"), voiceId: j.string().describe("ElevenLabs voice ID"), }), execute: async ({ content, voiceId }) => { const response = await fetch( `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, { method: "POST", headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY ?? "", "Content-Type": "application/json", }, body: JSON.stringify({ text: content, model_id: "eleven_monolingual_v1" }), } ); if (!response.ok) throw new Error(`ElevenLabs API error: ${response.statusText}`); const audioBuffer = await response.arrayBuffer(); return `Generated ${audioBuffer.byteLength} bytes of audio`; }, }); ``` -------------------------------- ### Define Orders Database Schema Source: https://context7.com/makenotion/workers-template/llms.txt Defines the schema for the 'orders' database, including properties like Title, Order ID, Status, and Total. This setup is part of the worker configuration. ```typescript import { Worker } from "@notionhq/workers"; import * as Builder from "@notionhq/workers/builder"; import * as Schema from "@notionhq/workers/schema"; const worker = new Worker(); export default worker; const orders = worker.database("orders", { type: "managed", initialTitle: "Orders", primaryKeyProperty: "Order ID", schema: { properties: { Title: Schema.title(), "Order ID": Schema.richText(), Status: Schema.select([ { name: "Pending", color: "yellow" }, { name: "Shipped", color: "blue" }, { name: "Delivered", color: "green" }, ]), Total: Schema.number("dollar"), }, }, }); ``` -------------------------------- ### Get Weather Data Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Fetch current weather information for a specified location using the OpenWeatherMap API. Requires an OpenWeatherMap API key set as an environment variable `OPENWEATHER_API_KEY`. ```typescript worker.tool("getWeather", { title: "Get Weather", description: "Get current weather for a location", schema: j.object({ location: j.string().describe("City name or zip code"), }), execute: async ({ location }) => { const response = await fetch( `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(location)}&appid=${process.env.OPENWEATHER_API_KEY}&units=metric`, ); if (!response.ok) throw new Error(`Weather API error: ${response.statusText}`); const data = await response.json(); return `${data.name}: ${data.main.temp}°C, ${data.weather[0].description}`; }, }); ``` -------------------------------- ### Create Podcast from Notion Page with ElevenLabs Source: https://github.com/makenotion/workers-template/blob/main/README.md Converts text content from a Notion page into audio using the ElevenLabs API. Requires an ElevenLabs API key and a voice ID. ```typescript worker.tool("createPodcast", { title: "Create Podcast from Page", description: "Convert page content to audio using ElevenLabs", schema: j.object({ content: j.string().describe("Page content to convert"), voiceId: j.string().describe("ElevenLabs voice ID"), }), execute: async ({ content, voiceId }) => { const response = await fetch( `https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`, { method: "POST", headers: { "xi-api-key": process.env.ELEVENLABS_API_KEY ?? "", "Content-Type": "application/json", }, body: JSON.stringify({ text: content, model_id: "eleven_monolingual_v1" }), }, ); if (!response.ok) throw new Error(`ElevenLabs API error: ${response.statusText}`); const audioBuffer = await response.arrayBuffer(); return `Generated ${audioBuffer.byteLength} bytes of audio`; }, }); ``` -------------------------------- ### Create a 'Say Hello' Tool Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Defines a simple tool for a Notion worker that returns a greeting. Requires importing `Worker` and `j` from `@notionhq/workers`. ```typescript import { Worker } from "@notionhq/workers"; import { j } from "@notionhq/workers/schema-builder"; const worker = new Worker(); export default worker; worker.tool("sayHello", { title: "Say Hello", description: "Returns a friendly greeting for the given name.", schema: j.object({ name: j.string().describe("The name to greet."), }), execute: ({ name }) => `Hello, ${name}!`, }); ``` -------------------------------- ### List Webhook URLs Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Use the CLI to list all configured webhook URLs for your workers. ```shell ntn workers webhooks list ``` -------------------------------- ### CLI Commands for Sync Management Source: https://context7.com/makenotion/workers-template/llms.txt Provides command-line interface commands to manage the sync processes, including resetting state and triggering syncs for full reloads, and monitoring sync status. ```bash ntn workers sync state reset ordersBackfill && ntn workers sync trigger ordersBackfill # full reload ntn workers sync status # monitor both syncs ``` -------------------------------- ### Set up a Managed Database for GitHub Repos Source: https://context7.com/makenotion/workers-template/llms.txt Define a managed database schema to store GitHub repository information, including name, ID, URL, and stars. ```typescript const repos = worker.database("repos", { type: "managed", initialTitle: "GitHub Repos", primaryKeyProperty: "Repo ID", schema: { properties: { Name: Schema.title(), "Repo ID": Schema.richText(), URL: Schema.url(), Stars: Schema.number(), }, }, }); ``` -------------------------------- ### Execute Worker Capability (TypeScript) Source: https://github.com/makenotion/workers-template/blob/main/AGENTS.md Example TypeScript script to execute a worker capability locally. It uses `child_process.execSync` to run the `ntn workers exec` command with specified input. This script is run using `npx tsx test.ts`. ```typescript import { execSync } from "child_process"; function exec(capability: string, input: Record) { const result = execSync( `ntn workers exec ${capability} --local -d '${JSON.stringify(input)}'`, { encoding: "utf-8" }, ); console.log(result); } exec("sayHello", { name: "World" }); ``` -------------------------------- ### Get Logs for a Specific Run (CLI) Source: https://github.com/makenotion/workers-template/blob/main/AGENTS.md Retrieve logs for a particular run using 'ntn workers runs logs '. You can also obtain logs for the latest run of any capability or a specific capability by combining 'list --plain' with other shell commands. ```shell ntn workers runs logs ``` ```shell ntn workers runs list --plain | head -n1 | cut -f1 | xargs -I{} ntn workers runs logs {} ``` ```shell ntn workers runs list --plain | grep tasksSync | head -n1 | cut -f1 | xargs -I{} ntn workers runs logs {} ``` -------------------------------- ### Create a Tool to Fetch GitHub Repositories Source: https://context7.com/makenotion/workers-template/llms.txt Implement a worker tool to fetch GitHub repositories using OAuth. The tool accepts an optional limit for the number of repositories to retrieve. ```typescript worker.tool("getGitHubRepos", { title: "Get GitHub Repos", description: "Fetch user's GitHub repositories", schema: j.object({ limit: j.number().describe("Maximum repos to return").nullable(), }), execute: async ({ limit = 10 }) => { const token = await githubAuth.accessToken(); const response = await fetch(`https://api.github.com/user/repos?per_page=${limit}`, { headers: { Authorization: `Bearer ${token}` }, }); if (!response.ok) throw new Error(`GitHub API error: ${response.statusText}`); const repos = await response.json(); return { repos: repos.map((r: { name: string; url: string }) => ({ name: r.name, url: r.url })) }; }, }); ``` -------------------------------- ### Enable/Disable Sync Capabilities CLI Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.md Manage sync capabilities using 'ntn workers capabilities'. Use 'list' to view all capabilities, 'disable ' to pause a sync, and 'enable ' to resume a paused sync. ```shell ntn workers capabilities list # show all capabilities ntn workers capabilities disable # pause a sync ntn workers capabilities enable # resume a sync ``` -------------------------------- ### Create Pacers for Rate Limiting Source: https://context7.com/makenotion/workers-template/llms.txt Pacers are used to manage external API rate limits for syncs. Declare a pacer with the desired number of allowed requests and interval, then call `await pacer.wait()` before each API request to ensure compliance. Examples show limits for 10 requests per second and 100 requests per minute. ```typescript import { Worker } from "@notionhq/workers"; const worker = new Worker(); export default worker; // Rate limit to 10 requests per second const stripeApi = worker.pacer("stripe", { allowedRequests: 10, intervalMs: 1000, }); // Rate limit to 100 requests per minute const salesforceApi = worker.pacer("salesforce", { allowedRequests: 100, intervalMs: 60_000, }); // Usage inside execute: // await stripeApi.wait(); // const data = await fetch("https://api.stripe.com/..."); ``` -------------------------------- ### Scaffold a New Notion Worker Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Scaffolds a new Notion worker project. Follow the prompts to set up your worker's structure. ```shell ntn workers new # Follow the prompts to scaffold your worker cd my-worker ``` -------------------------------- ### Test a Tool Locally Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Command to test a specific tool locally. ```shell ntn workers exec ``` -------------------------------- ### Implement Backfill Sync for Orders Source: https://context7.com/makenotion/workers-template/llms.txt Configures a manual 'replace' mode sync for the 'orders' database to perform an initial full dataset load. It fetches orders from a Shopify API endpoint. ```typescript // Backfill sync: manual trigger, replace mode for full dataset load worker.sync("ordersBackfill", { database: orders, mode: "replace", schedule: "manual", execute: async (state) => { const cursor = state?.cursor ?? null; await shopifyApi.wait(); const response = await fetch("https://shop.example.com/admin/api/orders.json", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: `query($after: String) { orders(first: 100, after: $after) { edges { node { id name status total } } pageInfo { hasNextPage endCursor } } }`, variables: { after: cursor }, }), }); const { data } = await response.json(); const { edges, pageInfo } = data.orders; return { changes: edges.map((e: { node: { id: string; name: string; status: string; total: number } }) => ({ type: "upsert" as const, key: e.node.id, properties: { Title: Builder.title(e.node.name), "Order ID": Builder.richText(e.node.id), Status: Builder.select(e.node.status), Total: Builder.number(e.node.total), }, })), hasMore: pageInfo.hasNextPage, nextState: pageInfo.hasNextPage ? { cursor: pageInfo.endCursor } : undefined, }; }, }); ``` -------------------------------- ### Scaffold a New Notion Worker Project Source: https://context7.com/makenotion/workers-template/llms.txt Use the `ntn workers new` command to create a new worker project. ```shell ntn workers new ``` -------------------------------- ### Configure User-Managed OAuth for GitHub Source: https://context7.com/makenotion/workers-template/llms.txt Set up OAuth for accessing third-party APIs like GitHub. Ensure environment variables for client ID and secret are configured. ```typescript import { Worker } from "@notionhq/workers"; import * as Schema from "@notionhq/workers/schema"; import * as Builder from "@notionhq/workers/builder"; import { j } from "@notionhq/workers/schema-builder"; const worker = new Worker(); export default worker; // User-managed OAuth (you own the OAuth app credentials) const githubAuth = worker.oauth("githubAuth", { name: "github-oauth", authorizationEndpoint: "https://github.com/login/oauth/authorize", tokenEndpoint: "https://github.com/login/oauth/access_token", scope: "repo user", clientId: process.env.GITHUB_CLIENT_ID ?? "", clientSecret: process.env.GITHUB_CLIENT_SECRET ?? "", }); ``` -------------------------------- ### Implement GitHub Repository Sync Source: https://context7.com/makenotion/workers-template/llms.txt Create a worker sync to periodically fetch GitHub repositories and upsert them into a managed Notion database. Ensure the `githubAuth` is configured. ```typescript worker.sync("githubReposSync", { database: repos, schedule: "1h", execute: async () => { const token = await githubAuth.accessToken(); const response = await fetch("https://api.github.com/user/repos?per_page=100", { headers: { Authorization: `Bearer ${token}` }, }); const repoList = await response.json(); return { changes: repoList.map((r: { id: number; name: string; html_url: string; stargazers_count: number }) => ({ type: "upsert" as const, key: String(r.id), properties: { Name: Builder.title(r.name), "Repo ID": Builder.richText(String(r.id)), URL: Builder.url(r.html_url), Stars: Builder.number(r.stargazers_count), }, })), hasMore: false, }; }, }); ``` -------------------------------- ### Configure GitHub OAuth Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Sets up OAuth for a Notion worker to authenticate with GitHub. Requires `clientId` and `clientSecret` from environment variables. ```typescript worker.oauth("githubAuth", { name: "github-oauth", authorizationEndpoint: "https://github.com/login/oauth/authorize", tokenEndpoint: "https://github.com/login/oauth/access_token", scope: "repo user", clientId: process.env.GITHUB_CLIENT_ID ?? "", clientSecret: process.env.GITHUB_CLIENT_SECRET ?? "", }); ``` -------------------------------- ### Sync CLI Commands Source: https://github.com/makenotion/workers-template/blob/main/README.md Command-line interface commands for managing Notion worker syncs, including status checks, triggering, previewing, resetting state, and enabling/disabling capabilities. ```shell ntn workers sync status # live-updating status ntn workers sync trigger --preview # preview output without writing to the database ntn workers sync trigger # trigger a real sync immediately ntn workers sync state reset # restart from scratch ntn workers capabilities disable # pause a sync ntn workers capabilities enable # resume a sync ``` -------------------------------- ### Configure GitHub OAuth Source: https://github.com/makenotion/workers-template/blob/main/README.md Set up the GitHub OAuth provider for your worker. Ensure GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET environment variables are set. ```typescript const githubAuth = worker.oauth("githubAuth", { name: "github-oauth", authorizationEndpoint: "https://github.com/login/oauth/authorize", tokenEndpoint: "https://github.com/login/oauth/access_token", scope: "repo user", clientId: process.env.GITHUB_CLIENT_ID ?? "", clientSecret: process.env.GITHUB_CLIENT_SECRET ?? "", }); ``` -------------------------------- ### Configure Replace Sync for Team Database Source: https://context7.com/makenotion/workers-template/llms.txt Sets up a managed Notion database for team members and defines a sync job using the 'replace' mode. This mode is efficient for small datasets as it fetches the full dataset each cycle and automatically deletes records not seen after `hasMore: false`. ```typescript import { Worker } from "@notionhq/workers"; import * as Builder from "@notionhq/workers/builder"; import * as Schema from "@notionhq/workers/schema"; const worker = new Worker(); export default worker; const teamDb = worker.database("team", { type: "managed", initialTitle: "Team Members", primaryKeyProperty: "Member ID", schema: { properties: { Name: Schema.title(), "Member ID": Schema.richText(), Role: Schema.select([{ name: "Engineer" }, { name: "Designer" }, { name: "PM" }]), }, }, }); const hrApi = worker.pacer("hrApi", { allowedRequests: 10, intervalMs: 1000 }); worker.sync("teamSync", { database: teamDb, mode: "replace", schedule: "1h", execute: async (state) => { const page = state?.page ?? 1; await hrApi.wait(); // Fetch from your HR system API const response = await fetch(`https://hr.example.com/api/employees?page=${page}&limit=100`); const { employees, hasMore } = await response.json(); return { changes: employees.map((emp: { id: string; name: string; role: string }) => ({ type: "upsert" as const, key: emp.id, properties: { Name: Builder.title(emp.name), "Member ID": Builder.richText(emp.id), Role: Builder.select(emp.role), }, })), hasMore, nextState: hasMore ? { page: page + 1 } : undefined, }; }, }); // CLI commands: // ntn workers deploy // ntn workers sync status // ntn workers sync trigger teamSync --preview # test without writing // ntn workers sync trigger teamSync # run immediately ``` -------------------------------- ### Declare Database, Pacer, and Sync for Data Synchronization Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.md This snippet illustrates how to declare a managed database with a specific schema, configure a pacer for rate-limited API calls, and define a sync function to fetch and upsert data into the database. It includes details on handling pagination and defining the next state for subsequent sync runs. ```typescript // 1. Declare a database const tasks = worker.database("tasks", { type: "managed", initialTitle: "Tasks", primaryKeyProperty: "Task ID", schema: { properties: { "Task Name": Schema.title(), "Task ID": Schema.richText(), Status: Schema.select([{ name: "Open" }, { name: "Done", color: "green" }]), }, }, }); // 2. Declare a pacer for the upstream API const myApi = worker.pacer("myApi", { allowedRequests: 10, intervalMs: 1000 }); // 3. Declare a sync worker.sync("tasksSync", { database: tasks, schedule: "30m", execute: async (state) => { await myApi.wait(); const { items, hasMore } = await fetchTasks(state?.page ?? 1); return { changes: items.map((item) => ({ type: "upsert" as const, key: item.id, properties: { "Task Name": Builder.title(item.name), "Task ID": Builder.richText(item.id), Status: Builder.select(item.status), }, })), hasMore, nextState: hasMore ? { page: (state?.page ?? 1) + 1 } : undefined, }; }, }); ``` -------------------------------- ### Trigger Sync Immediately Source: https://context7.com/makenotion/workers-template/llms.txt Manually initiate a sync task with the `ntn workers sync trigger` command. ```shell ntn workers sync trigger tasksSync ``` -------------------------------- ### Preview Sync Output Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Preview the output of a sync operation without writing to the database. This is useful for verifying sync logic and inspecting data. ```shell ntn workers sync trigger --preview ``` ```shell ntn workers sync trigger --preview --context '{"page":2}' ``` -------------------------------- ### Create a Tool with Schema Source: https://context7.com/makenotion/workers-template/llms.txt Tools are callable functions for Notion AI agents. Define input and output schemas using the `j` schema builder for TypeScript type inference and automatic property management. Ensure necessary environment variables like Twilio credentials are set for execution. ```typescript import { Worker } from "@notionhq/workers"; import { j } from "@notionhq/workers/schema-builder"; const worker = new Worker(); export default worker; worker.tool("sendSMS", { title: "Send SMS", description: "Send a text message to a phone number via Twilio", schema: j.object({ to: j.string().describe("Phone number in E.164 format"), message: j.string().describe("Message to send"), }), outputSchema: j.object({ success: j.boolean(), messageId: j.string(), }), execute: async ({ to, message }) => { const response = await fetch( `https://api.twilio.com/2010-04-01/Accounts/${process.env.TWILIO_ACCOUNT_SID}/Messages.json`, { method: "POST", headers: { Authorization: `Basic ${Buffer.from( `${process.env.TWILIO_ACCOUNT_SID}:${process.env.TWILIO_AUTH_TOKEN}` ).toString("base64")}`, "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ To: to, From: process.env.TWILIO_PHONE_NUMBER ?? "", Body: message, }), } ); if (!response.ok) throw new Error(`Twilio API error: ${response.statusText}`); const data = await response.json(); return { success: true, messageId: data.sid }; }, }); // Test locally: // ntn workers exec sendSMS --local -d '{"to": "+15551234567", "message": "Hello!"}' ``` -------------------------------- ### Paginated Incremental Sync Strategy with Deletes Source: https://github.com/makenotion/workers-template/blob/main/CLAUDE.alpha.md Implement this for APIs with change tracking. It fetches only changes since the last run and requires explicit deletion markers for removed records. The `nextState` should contain a cursor or similar value to track progress. ```typescript worker.sync("incrementalSync", { primaryKeyProperty: "ID", mode: "incremental", schema: { defaultName: "Records", properties: { Name: Schema.title(), ID: Schema.richText() } }, execute: async (state, { notion }) => { const { upserts, deletes, nextCursor } = await fetchChanges(state?.cursor); return { changes: [ ...upserts.map((item) => ({ type: "upsert", key: item.id, properties: { Name: Builder.title(item.name), ID: Builder.richText(item.id) }, })), ...deletes.map((id) => ({ type: "delete", key: id })), ], hasMore: Boolean(nextCursor), nextState: nextCursor ? { cursor: nextCursor } : undefined, }; }, }); ``` -------------------------------- ### Deploy Notion Worker Source: https://github.com/makenotion/workers-template/blob/main/README.alpha.md Deploys your configured Notion worker. This makes your worker available for use in Notion. ```shell ntn workers deploy ``` -------------------------------- ### Enable a Sync Capability Source: https://context7.com/makenotion/workers-template/llms.txt Resume a paused sync task by enabling its capability using `ntn workers capabilities enable`. ```shell ntn workers capabilities enable tasksSync ``` -------------------------------- ### Handle GitHub Push Events via Webhook Source: https://context7.com/makenotion/workers-template/llms.txt Set up a webhook endpoint to receive and process push events from GitHub. This handler verifies the signature and logs commit details. ```typescript worker.webhook("onGithubPush", { title: "GitHub Push Webhook", description: "Handles push events from GitHub repositories", execute: async (events) => { for (const event of events) { verifyGitHubSignature(event.rawBody, event.headers); const body = event.body; const ref = body.ref as string; const commits = body.commits as Array<{ message: string; author: { name: string } }>; console.log(`Push to ${ref}:`); for (const commit of commits) { console.log(` - ${commit.message} by ${commit.author.name}`); } } }, }); ``` -------------------------------- ### Define Tool Input Schema with Schema Builder Source: https://github.com/makenotion/workers-template/blob/main/README.md Uses the schema builder 'j' to define input parameters for a tool, including types, descriptions, and nullability. ```typescript import { j } from "@notionhq/workers/schema-builder"; schema: j.object({ query: j.string().describe("Search query"), limit: j.number().describe("Max results").nullable(), }) ``` -------------------------------- ### Preview Sync Trigger Source: https://context7.com/makenotion/workers-template/llms.txt Use `ntn workers sync trigger --preview` to see the potential changes from a sync without applying them. ```shell ntn workers sync trigger tasksSync --preview ``` -------------------------------- ### Configure Incremental Sync Mode Source: https://github.com/makenotion/workers-template/blob/main/README.md Set up an 'incremental' sync mode to process only changed records since the last run. Explicitly handle deletions. ```typescript worker.sync("eventsSync", { database: events, mode: "incremental", execute: async (state) => { await myApi.wait(); const { upserts, deletes, nextCursor } = await fetchChanges(state?.cursor); return { changes: [ ...upserts.map((item) => ({ type: "upsert" as const, key: item.id, properties: { Name: Builder.title(item.name), ID: Builder.richText(item.id) }, })), ...deletes.map((id) => ({ type: "delete" as const, key: id })) ], hasMore: Boolean(nextCursor), nextState: nextCursor ? { cursor: nextCursor } : undefined, }; }, }); ```