AutomationFlowsData & Sheets › Wf-04: Timeline Engine

Wf-04: Timeline Engine

WF-04: Timeline Engine. Uses supabase. Webhook trigger; 8 nodes.

Webhook trigger★★★★☆ complexity8 nodesSupabase
Data & Sheets Trigger: Webhook Nodes: 8 Complexity: ★★★★☆ Added:

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": "WF-04: Timeline Engine",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "wf04-timeline",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "notes": "Trigger with: POST https://YOUR-N8N-INSTANCE.onrender.com/webhook/wf04-timeline \u2014 or add a Schedule node for automatic runs."
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "metadata",
        "returnAll": true
      },
      "id": "fetch-all-entries",
      "name": "Fetch All Entries",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        450,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "notes": "Fetches all rows from the metadata table. For large tables (>10K rows), add pagination."
    },
    {
      "parameters": {
        "functionCode": "// Temporal clustering logic\n// Assigns each entry to a timeline_group based on date_estimated\n//\n// Customize the date ranges and group names for your project.\n// Current groups match a 3-era narrative structure:\n//   1974-1990 \u2014 Origin/founding period\n//   2019-2023 \u2014 Exploration/growth period\n//   2024-2026 \u2014 Current/integration period\n\nconst entries = $input.all();\nconst clustered = [];\n\nfor (const item of entries) {\n  const entry = item.json;\n  let timeline_group = '2024-2026'; // default to current period\n  \n  // Parse date_estimated field\n  const dateStr = entry.date_estimated || '';\n  \n  if (dateStr) {\n    let year = null;\n    \n    // Handle YYYY format\n    if (/^\\d{4}$/.test(dateStr)) {\n      year = parseInt(dateStr);\n    }\n    // Handle YYYY-MM-DD format\n    else if (/^\\d{4}-\\d{2}-\\d{2}$/.test(dateStr)) {\n      year = parseInt(dateStr.split('-')[0]);\n    }\n    // Handle any other format \u2014 extract 4-digit year\n    else {\n      const match = dateStr.match(/\\d{4}/);\n      if (match) year = parseInt(match[0]);\n    }\n    \n    // Map year to timeline_group\n    // CUSTOMIZE: Change these ranges to match your project's history\n    if (year) {\n      if (year >= 1974 && year <= 1990) {\n        timeline_group = '1974-1990';\n      } else if (year >= 2019 && year <= 2023) {\n        timeline_group = '2019-2023';\n      } else if (year >= 2024 && year <= 2026) {\n        timeline_group = '2024-2026';\n      }\n    }\n  }\n  \n  clustered.push({\n    json: {\n      id: entry.id,\n      file_name: entry.file_name,\n      timeline_group: timeline_group,\n      date_estimated: entry.date_estimated,\n      narrative_role: entry.narrative_role\n    }\n  });\n}\n\nreturn clustered;"
      },
      "id": "temporal-clustering",
      "name": "Temporal Clustering",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        650,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "// Narrative arc detection\n// Groups entries by timeline period and identifies dominant narrative roles\n//\n// narrative_role values follow a story arc:\n// setup \u2192 origin \u2192 exploration \u2192 conflict \u2192 realisation \u2192 integration\n//\n// CUSTOMIZE: Change the expected narrative roles to match your taxonomy.\n\nconst items = $input.all();\n\n// Group by timeline_group\nconst groups = {\n  '1974-1990': [],\n  '2019-2023': [],\n  '2024-2026': []\n};\n\nfor (const item of items) {\n  const group = item.json.timeline_group;\n  if (groups[group]) {\n    groups[group].push(item.json);\n  }\n}\n\n// Analyze narrative arc for each period\nconst analysis = {\n  '1974-1990': {\n    count: groups['1974-1990'].length,\n    roles: groups['1974-1990'].map(e => e.narrative_role).filter(Boolean),\n    dominant: 'origin'\n  },\n  '2019-2023': {\n    count: groups['2019-2023'].length,\n    roles: groups['2019-2023'].map(e => e.narrative_role).filter(Boolean),\n    dominant: 'exploration'\n  },\n  '2024-2026': {\n    count: groups['2024-2026'].length,\n    roles: groups['2024-2026'].map(e => e.narrative_role).filter(Boolean),\n    dominant: 'integration'\n  }\n};\n\nreturn [{\"json\": {\"analysis\": analysis, \"total_entries\": items.length, \"entries_to_update\": items}}];"
      },
      "id": "narrative-analysis",
      "name": "Narrative Arc Detection",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        850,
        300
      ]
    },
    {
      "parameters": {
        "batchSize": 1,
        "options": {}
      },
      "id": "split-batches",
      "name": "Split in Batches",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 1,
      "position": [
        1050,
        300
      ],
      "notes": "Processes one entry at a time. Increase batchSize if your Supabase plan supports higher concurrency."
    },
    {
      "parameters": {
        "functionCode": "const batch = $input.item.json;\nconst entries = batch.entries_to_update || [];\n\nif (entries.length > 0) {\n  const entry = entries[$node['Split in Batches'].context.currentRunIndex];\n  return [{json: entry}];\n}\n\nreturn [];"
      },
      "id": "extract-entry",
      "name": "Extract Entry",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1250,
        300
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "tableId": "metadata",
        "filterType": "string",
        "filterString": "id=eq.{{$json.id}}",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "timeline_group": "={{$json.timeline_group}}"
          }
        }
      },
      "id": "update-timeline-group",
      "name": "Update Timeline Group",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1450,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\": \"complete\", \"message\": \"Timeline clustering complete\", \"analysis\": {{$node['Narrative Arc Detection'].json.analysis}}, \"entries_updated\": {{$node['Narrative Arc Detection'].json.total_entries}}}"
      },
      "id": "respond-summary",
      "name": "Respond Summary",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1650,
        300
      ]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Fetch All Entries",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch All Entries": {
      "main": [
        [
          {
            "node": "Temporal Clustering",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Temporal Clustering": {
      "main": [
        [
          {
            "node": "Narrative Arc Detection",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Narrative Arc Detection": {
      "main": [
        [
          {
            "node": "Split in Batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split in Batches": {
      "main": [
        [
          {
            "node": "Extract Entry",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Entry": {
      "main": [
        [
          {
            "node": "Update Timeline Group",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Timeline Group": {
      "main": [
        [
          {
            "node": "Split in Batches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}

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

WF-04: Timeline Engine. Uses supabase. Webhook trigger; 8 nodes.

Source: https://github.com/batistai4724/zero-cost-ops/blob/main/workflows/WF-04-timeline-engine.json — 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

webhook - simulador PDV (fluxo). Uses supabase. Webhook trigger; 55 nodes.

Supabase
Data & Sheets

This workflow automates raw materials inventory management for businesses, eliminating manual stock updates, delayed material issue approvals, and missed low stock alerts. It ensures real-time stock t

Google Sheets, Gmail, Supabase +1
Data & Sheets

booking no duplicate. Uses httpRequest, supabase. Webhook trigger; 38 nodes.

HTTP Request, Supabase
Data & Sheets

Backend Erick. Uses supabase, n8n-nodes-evolution-api. Webhook trigger; 36 nodes.

Supabase, N8N Nodes Evolution Api
Data & Sheets

01-intake-storage. Uses airtable, supabase, stopAndError, httpRequest. Webhook trigger; 33 nodes.

Airtable, Supabase, Stop And Error +2