- Trigger
- Schedule Trigger, every 15 minutes
- Nodes
- 9
- Requires
- Notion, Google Calendar
- Category
- Sync
- From the course
- Notion, Airtable and Google Workspace
- Published
- 2026-09-10
Node types used
- Schedule Trigger
- Notion
- Loop Over Items
- Google Calendar
What it does
A task database with due dates is only useful if the due dates are somewhere you actually look, which for most people is a calendar. The naive version of this creates a duplicate event every time it runs, and leaves ghost events behind for tasks that have since been finished.
A Schedule Trigger every 15 minutes fans out to two independent branches. The create branch queries Notion for pages where Due Date is not empty, Status is not Done, and the Calendar Event ID property is empty, then loops one page at a time. Before it calls Google at all, it writes the string pending: plus the current execution id into that Calendar Event ID property, which takes the page out of the query the next run will make. Only then does it create an all-day Google Calendar event whose summary is the page title, whose start is the due date and whose end is the due date plus one day, and write the real event id back over the pending marker. The cleanup branch queries for pages where Status is Done, Calendar Event ID is not empty and does not contain pending:, deletes the matching Google Calendar event with errors set to continue so an already-deleted event does not stop the run, and clears the Calendar Event ID property on the page.
Only the title and the due date cross over. Nothing reads calendar edits back into Notion, so moving an event in Google Calendar does not change the Notion due date, and changing a due date in Notion does not move an event that already exists.
Node by node
- 1
Schedule Trigger Fires every 15 minutes and feeds both branches at once.
- 2
Tasks needing an event Queries the Notion database for pages where Due Date is not empty AND Status does not equal Done AND Calendar Event ID is empty. Returns all matches.
- 3
Loop Over Items Batch size one, so each task is claimed and created individually rather than as a bulk write.
- 4
Claim the task Writes pending:{{ $execution.id }} into the Calendar Event ID property before any calendar call. That single write is what stops a slow run and the next scheduled run from both creating an event for the same task.
- 5
Create calendar event Creates an event on the chosen calendar with allday yes, summary taken from the page title property, start set to the Due Date start value and end set to that date plus one day.
- 6
Write event ID back Overwrites the pending marker on the Notion page with the real Google Calendar event id, then returns to the loop.
- 7
Completed tasks with an event The other branch. Queries Notion for pages where Status equals Done AND Calendar Event ID is not empty AND does not contain pending:, so half-finished claims are never treated as real events.
- 8
Delete calendar event Deletes the event whose id is stored on the page. onError is set to continue on the regular output, so an event a person already deleted by hand does not fail the run.
- 9
Clear event ID Empties the Calendar Event ID property on the Notion page so the record is clean.
Setup after import
- 1
Create a Notion internal integration credential and select it on all five Notion nodes, then share the task database with that integration.
- 2
Create a Google Calendar OAuth2 credential and select it on both Google Calendar nodes.
- 3
Replace REPLACE_WITH_YOUR_CALENDAR_ID on both Google Calendar nodes with the calendar you want the events on.
- 4
Replace the Notion database URL on both query nodes. The export ships with https://www.notion.so/YOUR_NOTION_DATABASE_ID?v=YOUR_VIEW_ID.
- 5
Add three properties to the Notion database with these exact names: Due Date (date), Status (status, with a Done option) and Calendar Event ID (rich text). The filters reference the names literally.
Limits
Title and due date only. No description, no time of day, no attendees: every event is all day.
A due date changed after the event exists does not move the event.
Nothing reads Google Calendar back into Notion.
If a run fails between Claim the task and Write event ID back, the page keeps its pending: marker and is excluded from both branches until someone clears it.
Download the workflow
The 9 node export as n8n reads it. Credential ids, personal values and real endpoints are replaced with placeholders, so nothing here can reach an account that is not yours. No email, no account.
Download notion-tasks-to-google-calendar.jsonGet help with this in the community
If the import fails, if a node errors on the first run, or if you want to point this at a service it does not cover yet, post it in the free House of Loops community. Say which template it is and paste the error. Shannon Atkinson answers, and the answer stays there for the next person who hits the same thing.
Ask in the communityMore templates
One-way Airtable to Notion sync in n8n without duplicates
Every 15 minutes, pulls Airtable records changed in the last 20 minutes and updates or creates the matching Notion page, keyed on a stored Airtable ID.
- Trigger
- Schedule Trigger, every 15 minutes
- Nodes
- 9
Stripe webhook signature verification in n8n
A Webhook node with Raw Body on, a Code node that checks the Stripe-Signature HMAC, and a Postgres insert that ignores duplicate event ids.
- Trigger
- Stripe webhook, POST on the path /stripe
- Nodes
- 3
Stripe subscription lifecycle router in n8n
Verifies the Stripe signature, then routes customer.subscription events, invoice.paid and invoice.payment_failed to one branch per subscription status.
- Trigger
- Stripe webhook, POST on the path /stripe-subscription-lifecycle
- Nodes
- 15