_ _ ___ _____ ___ ___ _ _ ____ ___ _____ _ _ _ _ ____
| \ | |/ _ \_ _|_ _/ _ \| \ | | / ___|_ _|_ _| | | | | | | __ )
| \| | | | || | | | | | | \| | | | _ | | | | | |_| | | | | _ \
| |\ | |_| || | | | |_| | |\ | | |_| || | | | | _ | |_| | |_) |
|_| \_|\___/ |_| |___\___/|_| \_| \____|___| |_| |_| |_|\___/|____/
______ ___ _ ____
/ ___\ \ / / \ | |/ ___|
\___ \\ V /| \| | |
___) || | | |\ | |___
|____/ |_| |_| \_|\____|
Sync issues both ways. GitHub ↔ Notion. Notion GitHub Sync is a Node.js library for using a Notion database as a project management surface for GitHub repositories. It includes bidirectional issue sync, rate-limited Notion API helpers, built-in PM database schemas, and a CLI for setup and common workflows.
- GitHub → Notion: Open and recently-closed issues become Notion pages with status, labels, and a back-link to the issue.
- Notion → GitHub: Tasks created in Notion get filed as GitHub issues. The Notion page is updated with the new issue number and URL.
- Round-trip identity: Once linked, a row's
GitHub Numberis used as the stable identity for future syncs. - Three modes:
bidirectional(default),github-to-notion,notion-to-github— switch per-run.
- Rate-limited HTTP client sized around Notion's documented request budget to reduce accidental 429s.
- Exponential backoff with jitter on transient errors. Honors
Retry-Afterwhen present. - Typed errors:
AuthError,RateLimitError,NotFoundError,ValidationError—instanceofyour way out of guesswork. - Familiar @notionhq/client shape — common SDK surfaces wrapped with rate limiting and retry behavior.
- Interactive setup wizard:
npx notion-github-sync initwalks through token, parent page, and connection verification. - Built-in PM schemas: Tasks, Sprints, Epics, and Projects database templates are available when you want a quick starting point.
- Block factory:
heading,paragraph,bulletList,todoList,callout,code— readable, composable, no manual JSON. - Single-command database creation:
notion-github-sync create-db -k tasks -t "My Tasks".
- Bring your own MCP server: drop in a Notion MCP server and Bumba will auto-detect and route through it.
- Detection across transports: HTTP, IPC socket, Claude Desktop config, or explicit env.
- Graceful fallback: if MCP isn't configured or fails mid-call, the bridge can fall back to the direct Notion API. No MCP infrastructure is required to use the library.
- Three modes:
auto(default),mcp-only(strict),api-only(skip detection).
| Module | What it does |
|---|---|
NotionClient |
Rate-limited HTTP client with retry and typed errors |
NotionPublisher |
Page + database publisher with block factory and PM schemas |
GitHubIssueBridge |
Bidirectional GitHub Issues ↔ Notion sync |
NotionMCPBridge |
Optional MCP routing with API fallback |
runWizard |
Interactive setup wizard |
| CLI | init, verify, create-db, sync, mcp-status, config |
(requires Node ≥ 14; tested on 18, 20, 22)
# Install from npm (recommended)
npm install notion-github-sync
# Or clone for local development
git clone https://github.com/a2z2k26/notion-github-sync
cd notion-github-sync
npm install# 1. Configure your Notion integration
cp .env.example .env
# Add NOTION_API_KEY and NOTION_PARENT_PAGE_ID
# 2. Verify the connection
npx notion-github-sync verify
# 3. Or run the interactive wizard
npx notion-github-sync initYou'll need a Notion integration token and a parent page in your workspace shared with the integration. Full walkthrough: docs/notion-setup.md.
Create .env in your project root:
# Required
NOTION_API_KEY=<notion-token> # or ntn_...
NOTION_PARENT_PAGE_ID=<32-char-hex>
# For GitHub sync
GITHUB_TOKEN=<github-token> # PAT with `repo` scope
GITHUB_REPO=owner/name
NOTION_TASKS_DATABASE_ID=<32-char-hex>
# Optional
NOTION_DEBUG=true # verbose logging
NOTION_API_VERSION=2022-06-28 # default Notion API versionconst { GitHubIssueBridge } = require('notion-github-sync');
const bridge = new GitHubIssueBridge({
repo: 'owner/name',
databaseId: process.env.NOTION_TASKS_DATABASE_ID,
direction: 'bidirectional'
});
const result = await bridge.syncAll();
// {
// fromGitHub: { created: 12, updated: 3, skipped: 0, errors: 0 },
// toGitHub: { created: 2, updated: 5, skipped: 0, errors: 0 }
// }npx notion-github-sync sync -r owner/name -d <database-id>npx notion-github-sync create-db -k tasks -t "Engineering Tasks"The database ships with Status, Priority, Assignee, Due Date, GitHub Issue, GitHub Number, Labels, and Last Synced — the core columns the bridge expects.
const { NotionPublisher } = require('notion-github-sync');
const publisher = new NotionPublisher();
const { blocks } = publisher;
const page = await publisher.publishPage({
title: 'Q1 Roadmap',
blocks: [
blocks.heading('Goals', 1),
blocks.bulletList(['Ship v1', 'Onboard 10 customers', 'Launch docs']),
blocks.callout('Target: end of March'),
blocks.divider(),
blocks.heading('Risks', 2),
blocks.todoList([
{ text: 'API rate limits', checked: true },
{ text: 'Notion permissions', checked: false }
])
]
});
console.log(page.url);const { NotionClient } = require('notion-github-sync');
const client = new NotionClient(); // reads NOTION_API_KEY
// Familiar @notionhq/client-style calls. Requests go through the limiter.
await client.databases.query({ database_id: '...' });
await client.pages.create({ parent: { page_id: '...' }, properties: { ... } });
await client.blocks.children.append({ block_id: '...', children: [...] });The client retries 429 and 5xx automatically with exponential backoff and jitter. When it gives up, you get a typed error with a code and status:
const { errors } = require('notion-github-sync');
try {
await publisher.publishPage({ ... });
} catch (err) {
if (err instanceof errors.AuthError) return rotateToken();
if (err instanceof errors.RateLimitError) return queueForLater(err.retryAfterMs);
if (err instanceof errors.NotFoundError) return reportMissing();
throw err;
}const { assertConfig } = require('notion-github-sync');
const config = assertConfig(); // throws ConfigError on bad/missing env vars$ npx notion-github-sync --help
Commands:
init Run interactive setup wizard
verify Verify Notion API key and parent page access
create-db [options] Create a built-in PM database (tasks/sprints/epics/projects)
sync [options] Run GitHub↔Notion issue sync
config Show current configuration (API key masked)
Runnable scripts in examples/:
node examples/01-verify-connection.js # verify NOTION_API_KEY
node examples/02-create-page.js # create a page with mixed blocks
node examples/03-create-database.js # create a Tasks database
node examples/04-github-sync.js # pull GitHub issues into Notion
node examples/05-mcp-bridge.js # use MCP routing (with API fallback)┌────────────────────────────────────────────┐
│ Your code / CLI │
└────────────────────────────────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ NotionPublisher │ │ GitHubIssueBridge│
└──────────────────┘ └──────────────────┘
│ │
└─────────┬────────┘
▼
┌──────────────────┐
│ NotionClient │
│ rate-limit + retry│
│ + typed errors │
└──────────────────┘
│
▼
┌──────────────────┐
│ @notionhq/client │
└──────────────────┘
| Operation | Behavior |
|---|---|
| Burst calls | Auto-throttled to ~3 req/s — well inside Notion's limit |
| Transient failure | Retried with exp. backoff (default 4 attempts, 500ms→8s) |
| Rate limit | Honors Retry-After header if present, jittered fallback otherwise |
| Typed errors | Surface AuthError/RateLimitError/NotFoundError instead of raw Error |
For the GitHub sync, your Notion database needs these properties (the built-in tasks schema includes them all):
| Property | Type | Purpose |
|---|---|---|
Name (or any title) |
title |
Issue title |
GitHub Issue |
url |
Link back to the issue |
GitHub Number |
number |
Stable sync identity |
Status |
select |
Maps to GitHub state |
Last Synced |
date |
Set on every sync |
Labels |
multi_select |
Mirrors GitHub labels |
Generate a compatible database in one command:
npx notion-github-sync create-db -k tasks -t "Tasks"- Getting Started — 5-minute walkthrough
- API Reference — Module-by-module API reference
- Notion Setup — Notion-side integration walkthrough
- Contributing — How to contribute
- Security — How to report vulnerabilities
- Changelog — Release notes
MIT License — see LICENSE for details.
Built on @notionhq/client and @octokit/rest.