AutomationFlowsData & Sheets › Roll Overdue Notion Tasks Forward and Flag Stale Ones on a Schedule

Roll Overdue Notion Tasks Forward and Flag Stale Ones on a Schedule

ByKevin Yu @exekyute on n8n.io

This workflow runs every morning, scans a Notion tasks database for overdue, unfinished items, rolls their due dates forward, increments a roll counter, and marks tasks as stale once they have been rolled too many times. Runs every morning at 7:00 based on a schedule. Retrieves…

Cron / scheduled trigger★★★★☆ complexity8 nodesNotion
Data & Sheets Trigger: Cron / scheduled Nodes: 8 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #16802 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Roll overdue Notion tasks forward and flag ones rolled too often",
  "tags": [],
  "nodes": [
    {
      "name": "Every Morning at 7am",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        240,
        464
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 7
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "name": "Get Open Tasks",
      "type": "n8n-nodes-base.notion",
      "position": [
        416,
        464
      ],
      "parameters": {
        "simple": false,
        "options": {},
        "resource": "databasePage",
        "operation": "getAll",
        "returnAll": true,
        "databaseId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "name": "Roll Overdue Dates and Flag Stale",
      "type": "n8n-nodes-base.code",
      "position": [
        720,
        464
      ],
      "parameters": {
        "jsCode": "// ==== CONFIG: edit these to match your Notion database ====\nconst DUE_PROPERTY    = 'Due';      // Date property that holds the deadline\nconst STATUS_PROPERTY = 'Status';   // Status or Select property that holds task state\nconst DONE_VALUES     = ['Done', 'Complete', 'Completed', 'Cancelled', 'Archived']; // states treated as finished, never rolled\nconst ROLLED_PROPERTY = 'Rolled';   // Number property: how many times this task has been rolled\nconst STALE_PROPERTY  = 'Stale';    // Checkbox property: set true once a task is rolled past the threshold\nconst STALE_THRESHOLD = 3;          // flag Stale once a task has been rolled MORE than this many times\nconst ROLL_TO         = 'today';    // 'today' | 'nextBusinessDay'\n// ==========================================================\n\n// \"Today\" comes from n8n's $now (a Luxon DateTime), taken at calendar-date granularity.\nconst todayISO = $now.toISODate(); // 'yyyy-MM-dd'\n\nconst doneSet = new Set(DONE_VALUES.map((v) => String(v).trim().toLowerCase()));\n\nfunction propOf(page, name) {\n  return ((page && page.properties) || {})[name];\n}\n\nfunction readDueDate(page) {\n  const p = propOf(page, DUE_PROPERTY);\n  if (!p) return '';\n  if (p.type === 'date' && p.date && p.date.start) return String(p.date.start);\n  if (p.type === 'formula' && p.formula && p.formula.date && p.formula.date.start) return String(p.formula.date.start);\n  return '';\n}\n\nfunction readStatus(page) {\n  const p = propOf(page, STATUS_PROPERTY);\n  if (!p) return '';\n  if (p.type === 'status') return (p.status && p.status.name) || '';\n  if (p.type === 'select') return (p.select && p.select.name) || '';\n  return '';\n}\n\nfunction readRolled(page) {\n  const p = propOf(page, ROLLED_PROPERTY);\n  if (p && p.type === 'number' && p.number != null) return Number(p.number);\n  return 0;\n}\n\nfunction readTitle(page) {\n  const props = (page && page.properties) || {};\n  for (const k of Object.keys(props)) {\n    if (props[k] && props[k].type === 'title') {\n      return (props[k].title || []).map((x) => x.plain_text).join('');\n    }\n  }\n  return '';\n}\n\nfunction nextBusinessDay(iso) {\n  // Snap a weekend date up to Monday. Weekday dates are returned unchanged.\n  const d = new Date(iso + 'T00:00:00Z');\n  let dow = d.getUTCDay(); // 0 = Sunday, 6 = Saturday\n  while (dow === 0 || dow === 6) {\n    d.setUTCDate(d.getUTCDate() + 1);\n    dow = d.getUTCDay();\n  }\n  return d.toISOString().slice(0, 10);\n}\n\nconst rollTargetISO = ROLL_TO === 'nextBusinessDay' ? nextBusinessDay(todayISO) : todayISO;\n\nconst out = [];\nfor (const item of items) {\n  const page = item.json;\n\n  const dueStart = readDueDate(page);\n  if (!dueStart) continue;                 // no due date, nothing to roll\n  const dueDateOnly = dueStart.slice(0, 10);\n  if (dueDateOnly >= todayISO) continue;    // due today or in the future, not overdue\n\n  const status = readStatus(page);\n  if (doneSet.has(status.trim().toLowerCase())) continue; // finished tasks are never touched\n\n  if (rollTargetISO === dueDateOnly) continue; // no-op guard: skip if the date would not change\n\n  const newRolled = readRolled(page) + 1;\n  const stale = newRolled > STALE_THRESHOLD;\n\n  out.push({\n    json: {\n      pageId: page.id,\n      taskName: readTitle(page),\n      statusValue: status,\n      oldDue: dueDateOnly,\n      newDate: rollTargetISO,\n      newRolled: newRolled,\n      stale: stale,\n    },\n  });\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "name": "Update Rolled Tasks",
      "type": "n8n-nodes-base.notion",
      "onError": "continueRegularOutput",
      "position": [
        1088,
        464
      ],
      "parameters": {
        "pageId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.pageId }}"
        },
        "options": {},
        "resource": "databasePage",
        "operation": "update",
        "propertiesUi": {
          "propertyValues": [
            {
              "key": "Due|date",
              "date": "={{ $json.newDate }}",
              "includeTime": false
            },
            {
              "key": "Rolled|number",
              "numberValue": "={{ $json.newRolled }}"
            },
            {
              "key": "Stale|checkbox",
              "checkboxValue": "={{ $json.stale }}"
            }
          ]
        }
      },
      "credentials": {
        "notionApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "name": "Overview Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -400,
        128
      ],
      "parameters": {
        "width": 560,
        "height": 768,
        "content": "## Roll overdue Notion tasks forward and flag ones rolled too often\n\n### How it works\n\n1. On a daily schedule, it reads every row from the Notion database you choose.\n2. A Code node finds rows whose due date is in the past and whose status is not done.\n3. Each overdue task has its due date rolled forward to today, or the next business day.\n4. A rolled counter is incremented, and a Stale flag is set once a task has been rolled past a threshold.\n5. Only the rows that changed are written back in place, so finished and not-yet-due tasks are never touched.\n\n### Setup steps\n\n- [ ] Connect a Notion credential and share the target database with the integration.\n- [ ] In Get Open Tasks, select the database that holds your tasks.\n- [ ] In Roll Overdue Dates and Flag Stale, set the property names, DONE_VALUES, STALE_THRESHOLD, and ROLL_TO at the top of the code.\n- [ ] In Update Rolled Tasks, map the Due, Rolled, and Stale property values to your own property names.\n\n### Customization\n\nSet ROLL_TO to today or nextBusinessDay, change STALE_THRESHOLD to control how many rolls make a task stale, and adjust Every Morning at 7am to run on any schedule you prefer."
      },
      "typeVersion": 1
    },
    {
      "name": "Read Section Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        192,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 376,
        "height": 364,
        "content": "## Read open tasks on a schedule\n\nRuns each morning and pulls every row from the database with full property data."
      },
      "typeVersion": 1
    },
    {
      "name": "Roll Section Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        608,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 364,
        "content": "## Find overdue tasks and roll them\n\nSkips finished and not-yet-due rows, rolls each overdue task forward, and flags the ones rolled too often."
      },
      "typeVersion": 1
    },
    {
      "name": "Update Section Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        272
      ],
      "parameters": {
        "color": 7,
        "width": 316,
        "height": 364,
        "content": "## Write the rolled tasks back\n\nUpdates only the rows that changed: the new due date, the incremented counter, and the Stale flag."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Get Open Tasks": {
      "main": [
        [
          {
            "node": "Roll Overdue Dates and Flag Stale",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every Morning at 7am": {
      "main": [
        [
          {
            "node": "Get Open Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Roll Overdue Dates and Flag Stale": {
      "main": [
        [
          {
            "node": "Update Rolled Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow runs every morning, scans a Notion tasks database for overdue, unfinished items, rolls their due dates forward, increments a roll counter, and marks tasks as stale once they have been rolled too many times. Runs every morning at 7:00 based on a schedule. Retrieves…

Source: https://n8n.io/workflows/16802/ — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

WorkFlow 05. Uses notion, httpRequest. Scheduled trigger; 44 nodes.

Notion, HTTP Request
Data & Sheets

WorkFlow 08. Uses notion, httpRequest. Scheduled trigger; 37 nodes.

Notion, HTTP Request
Data & Sheets

WorkFlow 01. Uses notion. Scheduled trigger; 30 nodes.

Notion
Data & Sheets

This template is designed for social media managers, content creators, data analysts, and anyone who wants to automatically save and analyze their Meta Threads posts in Notion.

HTTP Request, Notion
Data & Sheets

WorkFlow 02. Uses notion. Scheduled trigger; 23 nodes.

Notion