Automatically syncs events from a Notion database to a Google Calendar. You fill in events on Notion; this script keeps Google Calendar up to date automatically, running every 30 minutes. Notion is the sole source of truth — all events flow one-way from Notion into Google Calendar only.
- Node.js v18 or later (
node --versionto check) - A Notion internal integration with read access to your database:
- Go to notion.so/my-integrations → New integration
- Copy the "Internal Integration Token"
- Open your Notion database →
...menu → Connections → add your integration
- A Google Cloud project with OAuth credentials:
- Go to console.cloud.google.com → New project
- Enable the Google Calendar API (APIs & Services → Library)
- Create OAuth 2.0 credentials (APIs & Services → Credentials → Create Credentials → OAuth client ID → Web application)
- Add
http://localhost:3000/auth/callbackas an Authorised redirect URI - Copy the Client ID and Client Secret
- Find your target Calendar ID in Google Calendar → Settings → your calendar → "Calendar ID"
# 1. Clone and install
git clone <repo-url>
cd notioncal-to-gcal
npm install
# 2. Configure environment
cp .env.example .env
# Edit .env and fill in all six values
# 3. Authorise Google Calendar (one-time only)
node scripts/setup-auth.js
# Open the printed URL in your browser and grant permission
# tokens.json will be saved automatically — stop the server with Ctrl+C
# 4. Start syncing
node index.jsEach sync run:
- Fetches every page from your Notion database
- Creates a Google Calendar event for any page that has never been synced
- Updates the Google event for any page that was already synced
- Deletes Google events whose Notion pages were removed or archived
- Skips pages with no date set (logs a warning)
The mapping between Notion page IDs and Google event IDs is stored in sync-state.json (never committed to git).
All field translation logic lives in sync/mapFields.js. To add a new field:
- Open
sync/mapFields.js - Find the relevant
// TODO: add [field] herecomment - Uncomment and adapt the example code
- That's it — no other files need changing
index.js Entry point: load tokens, start cron scheduler
scripts/setup-auth.js One-time Google OAuth browser flow
auth/google.js OAuth2 client, token helpers
auth/notion.js Notion client
routes/auth.js Express routes for OAuth callback
sync/fetchNotion.js Paginated fetch from Notion database
sync/mapFields.js Notion page → Google Calendar event (edit to add fields)
sync/googleCalendar.js Google Calendar create / update / delete wrappers
sync/stateManager.js Load and save sync-state.json
sync/syncRunner.js Core sync orchestration