{
  "workflow": {
    "id": 10891,
    "name": "Predict and Forecast HDB Flat Prices with GPT-4o and Google Sheets Analytics",
    "views": 62,
    "recentViews": 0,
    "totalViews": 62,
    "createdAt": "2025-11-16T16:39:55.550Z",
    "description": "## How It Works\nThe workflow runs on a monthly trigger to collect both current-year and multi-year historical HDB data. Once fetched, all datasets are merged with aligned fields to produce a unified table. The system then applies cleaning and normalization rules to ensure consistent scales and comparable values. After preprocessing, it performs pattern mining, anomaly checks, and time-series analysis to extract trends and forecast signals. An AI agent, integrating OpenAI GPT-4, statistical tools, and calculator nodes, synthesizes these results into coherent insights. The final predictions are formatted and automatically written to Google Sheets for reporting and downstream use.\n\n## Setup Steps\n1) Configure fetch nodes to pull current-year HDB data and three years of historical records.\n2) Align and map column names across all datasets.\n3) Set normalization and standardization parameters in the cleaning node.\n4) Add your OpenAI API key (GPT-4) and link the model, forecasting tool, and calculator nodes.\n5) Authorize Google Sheets and configure sheet and cell mappings for automated export.\n \n\n\n## Prerequisites\n- Historical data source with API access (3+ years of records)\n- OpenAI API key for GPT-4 model\n- Google Sheets account with API credentials\n- Basic understanding of time series data\n\n## Use Cases\n**Real Estate:** Forecast property prices using multi-year historical HDB/market data with confidence intervals\n**Finance:** Predict market trends by aggregating years of transaction or pricing records\n\n## Customization\n**Data Source:** Replace HDB/fetch nodes with stock prices, sensor data, sales records, or any historical dataset\n**Analysis Window:** Adjust years fetched (2-5 years) based on data availability and prediction horizon\n\n\n## Benefits\n**Automation:** Monthly scheduling eliminates manual data gathering and analysis\n**Consolidation:** Merges fragmented year-by-year data into unified historical view\n\n",
    "workflow": {
      "id": "pep0km4DZl5WamRP",
      "meta": {
        "instanceId": "b91e510ebae4127f953fd2f5f8d40d58ca1e71c746d4500c12ae86aad04c1502"
      },
      "name": "GPT-4o HDB Flat Price Prediction and Forecasting System",
      "tags": [],
      "nodes": [
        {
          "id": "c4200cc9-166c-49c7-ac22-7935a21ac7ff",
          "name": "Monthly Data Collection Trigger",
          "type": "n8n-nodes-base.scheduleTrigger",
          "position": [
            -448,
            288
          ],
          "parameters": {
            "rule": {
              "interval": [
                {
                  "field": "months",
                  "triggerAtHour": 2
                }
              ]
            }
          },
          "typeVersion": 1.2
        },
        {
          "id": "1e8ee0eb-53a6-465d-995b-f2b9e9cbdfcf",
          "name": "Workflow Configuration",
          "type": "n8n-nodes-base.set",
          "position": [
            -224,
            288
          ],
          "parameters": {
            "options": {},
            "assignments": {
              "assignments": [
                {
                  "id": "id-1",
                  "name": "hdbApiBaseUrl",
                  "type": "string",
                  "value": "https://data.gov.sg/api/action/datastore_search"
                },
                {
                  "id": "id-2",
                  "name": "currentYear",
                  "type": "string",
                  "value": "2024"
                },
                {
                  "id": "id-3",
                  "name": "historicalYear1",
                  "type": "string",
                  "value": "2023"
                },
                {
                  "id": "id-4",
                  "name": "historicalYear2",
                  "type": "string",
                  "value": "2022"
                },
                {
                  "id": "id-5",
                  "name": "historicalYear3",
                  "type": "string",
                  "value": "2021"
                },
                {
                  "id": "id-6",
                  "name": "resourceId",
                  "type": "string",
                  "value": "<__PLACEHOLDER_VALUE__HDB Resale Flat Prices Resource ID__>"
                }
              ]
            },
            "includeOtherFields": true
          },
          "typeVersion": 3.4
        },
        {
          "id": "71b7ffea-e80f-48b5-bf67-bdb266b89160",
          "name": "Fetch Current Year HDB Data",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            0,
            48
          ],
          "parameters": {
            "url": "={{ $('Workflow Configuration').first().json.hdbApiBaseUrl }}?resource_id={{ $('Workflow Configuration').first().json.resourceId }}&filters={\"month\":\"{{ $('Workflow Configuration').first().json.currentYear }}\"}&limit=10000",
            "options": {
              "response": {
                "response": {
                  "responseFormat": "json"
                }
              }
            }
          },
          "typeVersion": 4.3
        },
        {
          "id": "0018749a-c44e-48a7-b3ee-524928f1cde2",
          "name": "Fetch Historical Data Year 1",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            0,
            192
          ],
          "parameters": {
            "url": "={{ $('Workflow Configuration').first().json.hdbApiBaseUrl }}?resource_id={{ $('Workflow Configuration').first().json.resourceId }}&filters={\"month\":\"{{ $('Workflow Configuration').first().json.historicalYear1 }}\"}&limit=10000",
            "options": {
              "response": {
                "response": {
                  "responseFormat": "json"
                }
              }
            }
          },
          "typeVersion": 4.3
        },
        {
          "id": "d794c0df-55be-4e92-9064-ac56f4902e8f",
          "name": "Fetch Historical Data Year 2",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            0,
            384
          ],
          "parameters": {
            "url": "={{ $('Workflow Configuration').first().json.hdbApiBaseUrl }}?resource_id={{ $('Workflow Configuration').first().json.resourceId }}&filters={\"month\":\"{{ $('Workflow Configuration').first().json.historicalYear2 }}\"}&limit=10000",
            "options": {
              "response": {
                "response": {
                  "responseFormat": "json"
                }
              }
            }
          },
          "typeVersion": 4.3
        },
        {
          "id": "7d012cd6-915e-4959-8e80-9d6d4b595317",
          "name": "Fetch Historical Data Year 3",
          "type": "n8n-nodes-base.httpRequest",
          "position": [
            0,
            576
          ],
          "parameters": {
            "url": "={{ $('Workflow Configuration').first().json.hdbApiBaseUrl }}?resource_id={{ $('Workflow Configuration').first().json.resourceId }}&filters={\"month\":\"{{ $('Workflow Configuration').first().json.historicalYear3 }}\"}&limit=10000",
            "options": {
              "response": {
                "response": {
                  "responseFormat": "json"
                }
              }
            }
          },
          "typeVersion": 4.3
        },
        {
          "id": "dd7428f8-e09f-47de-bad7-6513e2bc239a",
          "name": "Merge All Historical Data",
          "type": "n8n-nodes-base.merge",
          "position": [
            224,
            256
          ],
          "parameters": {
            "numberInputs": 4
          },
          "typeVersion": 3.2
        },
        {
          "id": "a50473f3-52de-4f47-a84a-85324aa00b85",
          "name": "Data Cleaning and Normalization",
          "type": "n8n-nodes-base.code",
          "position": [
            448,
            288
          ],
          "parameters": {
            "jsCode": "// Data Cleaning and Normalization for HDB Resale Data\n// This code processes raw HDB data to ensure quality and consistency\n\nconst items = $input.all();\nconst cleanedData = [];\nconst seenRecords = new Set();\n\n// Helper function to normalize town names\nfunction normalizeTown(town) {\n  if (!town) return null;\n  return town.toString().trim().toUpperCase();\n}\n\n// Helper function to standardize flat types\nfunction standardizeFlatType(flatType) {\n  if (!flatType) return null;\n  const normalized = flatType.toString().trim().toUpperCase();\n  \n  // Standardize common variations\n  const typeMap = {\n    '1 ROOM': '1-ROOM',\n    '2 ROOM': '2-ROOM',\n    '3 ROOM': '3-ROOM',\n    '4 ROOM': '4-ROOM',\n    '5 ROOM': '5-ROOM',\n    'EXECUTIVE': 'EXECUTIVE',\n    'MULTI-GENERATION': 'MULTI-GENERATION',\n    'MULTI GENERATION': 'MULTI-GENERATION'\n  };\n  \n  return typeMap[normalized] || normalized;\n}\n\n// Helper function to parse price\nfunction parsePrice(price) {\n  if (!price) return null;\n  \n  // Remove currency symbols, commas, and whitespace\n  const cleaned = price.toString().replace(/[$,\\s]/g, '');\n  const parsed = parseFloat(cleaned);\n  \n  return isNaN(parsed) ? null : parsed;\n}\n\n// Helper function to parse date\nfunction parseDate(dateStr) {\n  if (!dateStr) return null;\n  \n  try {\n    // Handle various date formats (YYYY-MM, YYYY/MM, etc.)\n    const cleaned = dateStr.toString().trim();\n    const date = new Date(cleaned);\n    \n    if (isNaN(date.getTime())) return null;\n    return date.toISOString();\n  } catch (e) {\n    return null;\n  }\n}\n\n// Helper function to parse floor area\nfunction parseFloorArea(area) {\n  if (!area) return null;\n  \n  const cleaned = area.toString().replace(/[^0-9.]/g, '');\n  const parsed = parseFloat(cleaned);\n  \n  return isNaN(parsed) ? null : parsed;\n}\n\n// Process each item\nfor (const item of items) {\n  const data = item.json;\n  \n  // Skip if data is not an object\n  if (typeof data !== 'object' || data === null) continue;\n  \n  // Extract and clean fields\n  const month = data.month || data.Month || data.MONTH;\n  const town = normalizeTown(data.town || data.Town || data.TOWN);\n  const flatType = standardizeFlatType(data.flat_type || data.flatType || data.FlatType || data.flat_model);\n  const block = data.block || data.Block || data.BLOCK;\n  const streetName = data.street_name || data.streetName || data.StreetName;\n  const storeyRange = data.storey_range || data.storeyRange || data.StoreyRange;\n  const floorArea = parseFloorArea(data.floor_area_sqm || data.floorArea || data.FloorArea);\n  const flatModel = data.flat_model || data.flatModel || data.FlatModel;\n  const leaseCommenceDate = data.lease_commence_date || data.leaseCommenceDate || data.LeaseCommenceDate;\n  const remainingLease = data.remaining_lease || data.remainingLease || data.RemainingLease;\n  const resalePrice = parsePrice(data.resale_price || data.resalePrice || data.ResalePrice || data.price);\n  \n  // Validate required fields\n  if (!month || !town || !flatType || !resalePrice) {\n    continue; // Skip invalid records\n  }\n  \n  // Additional validation\n  if (resalePrice <= 0 || resalePrice > 2000000) continue; // Unrealistic prices\n  if (floorArea && (floorArea < 20 || floorArea > 300)) continue; // Unrealistic floor areas\n  \n  // Create unique identifier to detect duplicates\n  const recordId = `${month}_${town}_${flatType}_${block}_${streetName}_${resalePrice}`;\n  \n  if (seenRecords.has(recordId)) {\n    continue; // Skip duplicate\n  }\n  \n  seenRecords.add(recordId);\n  \n  // Create cleaned record\n  const cleanedRecord = {\n    month: parseDate(month),\n    town: town,\n    flat_type: flatType,\n    block: block ? block.toString().trim() : null,\n    street_name: streetName ? streetName.toString().trim() : null,\n    storey_range: storeyRange ? storeyRange.toString().trim() : null,\n    floor_area_sqm: floorArea,\n    flat_model: flatModel ? flatModel.toString().trim() : null,\n    lease_commence_date: leaseCommenceDate ? parseInt(leaseCommenceDate) : null,\n    remaining_lease: remainingLease ? remainingLease.toString().trim() : null,\n    resale_price: resalePrice,\n    // Add derived fields\n    year: month ? new Date(month).getFullYear() : null,\n    price_per_sqm: floorArea && resalePrice ? Math.round(resalePrice / floorArea) : null\n  };\n  \n  cleanedData.push({ json: cleanedRecord });\n}\n\nconsole.log(`Data Cleaning Summary:`);\nconsole.log(`- Input records: ${items.length}`);\nconsole.log(`- Cleaned records: ${cleanedData.length}`);\nconsole.log(`- Duplicates removed: ${items.length - cleanedData.length - (items.length - seenRecords.size)}`);\nconsole.log(`- Invalid records filtered: ${items.length - cleanedData.length}`);\n\nif (cleanedData.length === 0) {\n  throw new Error('No valid records after cleaning. Please check the input data format.');\n}\n\nreturn cleanedData;"
          },
          "typeVersion": 2
        },
        {
          "id": "87773176-547d-493c-9cce-d1713ea24a4d",
          "name": "Statistical Pattern Mining",
          "type": "n8n-nodes-base.code",
          "position": [
            688,
            96
          ],
          "parameters": {
            "jsCode": "// Statistical Pattern Mining for HDB Flat Price Data\n// This code performs comprehensive statistical analysis on the cleaned HDB data\n\nconst items = $input.all();\n\nif (!items || items.length === 0) {\n  return [];\n}\n\n// Helper function to calculate statistical measures\nfunction calculateStats(values) {\n  if (!values || values.length === 0) return null;\n  \n  const sorted = values.slice().sort((a, b) => a - b);\n  const n = sorted.length;\n  \n  // Mean\n  const mean = values.reduce((sum, val) => sum + val, 0) / n;\n  \n  // Median\n  const median = n % 2 === 0 \n    ? (sorted[n/2 - 1] + sorted[n/2]) / 2 \n    : sorted[Math.floor(n/2)];\n  \n  // Standard Deviation\n  const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / n;\n  const stdDev = Math.sqrt(variance);\n  \n  // Quartiles\n  const q1Index = Math.floor(n * 0.25);\n  const q3Index = Math.floor(n * 0.75);\n  const q1 = sorted[q1Index];\n  const q3 = sorted[q3Index];\n  \n  return {\n    mean: Math.round(mean * 100) / 100,\n    median: Math.round(median * 100) / 100,\n    stdDev: Math.round(stdDev * 100) / 100,\n    q1: Math.round(q1 * 100) / 100,\n    q3: Math.round(q3 * 100) / 100,\n    min: sorted[0],\n    max: sorted[n - 1],\n    count: n\n  };\n}\n\n// Group data by flat type and town\nconst groupedData = {};\nconst timeSeriesData = {};\n\nitems.forEach(item => {\n  const data = item.json;\n  const flatType = data.flat_type || 'Unknown';\n  const town = data.town || 'Unknown';\n  const price = parseFloat(data.resale_price);\n  const floorLevel = parseFloat(data.floor_area_sqm) || 0;\n  const leaseRemaining = parseFloat(data.remaining_lease_years) || 0;\n  const flatSize = parseFloat(data.floor_area_sqm) || 0;\n  const year = data.year || new Date(data.month).getFullYear();\n  const month = data.month;\n  \n  // Group by flat type and town\n  const key = `${flatType}_${town}`;\n  if (!groupedData[key]) {\n    groupedData[key] = {\n      flatType,\n      town,\n      prices: [],\n      floorLevels: [],\n      leaseRemaining: [],\n      flatSizes: []\n    };\n  }\n  \n  groupedData[key].prices.push(price);\n  groupedData[key].floorLevels.push(floorLevel);\n  groupedData[key].leaseRemaining.push(leaseRemaining);\n  groupedData[key].flatSizes.push(flatSize);\n  \n  // Time series grouping\n  if (!timeSeriesData[year]) {\n    timeSeriesData[year] = { prices: [], months: {} };\n  }\n  timeSeriesData[year].prices.push(price);\n  \n  if (month) {\n    const monthKey = month.substring(0, 7); // YYYY-MM\n    if (!timeSeriesData[year].months[monthKey]) {\n      timeSeriesData[year].months[monthKey] = [];\n    }\n    timeSeriesData[year].months[monthKey].push(price);\n  }\n});\n\n// Calculate statistics by flat type and town\nconst statisticalInsights = [];\n\nfor (const [key, data] of Object.entries(groupedData)) {\n  const priceStats = calculateStats(data.prices);\n  const floorStats = calculateStats(data.floorLevels);\n  const leaseStats = calculateStats(data.leaseRemaining);\n  const sizeStats = calculateStats(data.flatSizes);\n  \n  // Calculate correlations (simplified Pearson correlation)\n  function correlation(x, y) {\n    if (x.length !== y.length || x.length === 0) return 0;\n    const n = x.length;\n    const meanX = x.reduce((a, b) => a + b, 0) / n;\n    const meanY = y.reduce((a, b) => a + b, 0) / n;\n    \n    let numerator = 0;\n    let denomX = 0;\n    let denomY = 0;\n    \n    for (let i = 0; i < n; i++) {\n      const dx = x[i] - meanX;\n      const dy = y[i] - meanY;\n      numerator += dx * dy;\n      denomX += dx * dx;\n      denomY += dy * dy;\n    }\n    \n    if (denomX === 0 || denomY === 0) return 0;\n    return numerator / Math.sqrt(denomX * denomY);\n  }\n  \n  const priceFloorCorr = correlation(data.prices, data.floorLevels);\n  const priceLeaseCorr = correlation(data.prices, data.leaseRemaining);\n  const priceSizeCorr = correlation(data.prices, data.flatSizes);\n  \n  statisticalInsights.push({\n    flatType: data.flatType,\n    town: data.town,\n    priceStatistics: priceStats,\n    floorLevelStats: floorStats,\n    leaseRemainingStats: leaseStats,\n    flatSizeStats: sizeStats,\n    correlations: {\n      priceVsFloorLevel: Math.round(priceFloorCorr * 1000) / 1000,\n      priceVsLeaseRemaining: Math.round(priceLeaseCorr * 1000) / 1000,\n      priceVsFlatSize: Math.round(priceSizeCorr * 1000) / 1000\n    }\n  });\n}\n\n// Calculate year-over-year growth rates\nconst years = Object.keys(timeSeriesData).sort();\nconst yoyGrowth = [];\n\nfor (let i = 1; i < years.length; i++) {\n  const prevYear = years[i - 1];\n  const currYear = years[i];\n  const prevAvg = timeSeriesData[prevYear].prices.reduce((a, b) => a + b, 0) / timeSeriesData[prevYear].prices.length;\n  const currAvg = timeSeriesData[currYear].prices.reduce((a, b) => a + b, 0) / timeSeriesData[currYear].prices.length;\n  const growthRate = ((currAvg - prevAvg) / prevAvg) * 100;\n  \n  yoyGrowth.push({\n    fromYear: prevYear,\n    toYear: currYear,\n    previousAvgPrice: Math.round(prevAvg * 100) / 100,\n    currentAvgPrice: Math.round(currAvg * 100) / 100,\n    growthRate: Math.round(growthRate * 100) / 100\n  });\n}\n\n// Detect seasonal patterns (monthly averages)\nconst seasonalPatterns = {};\nfor (const [year, data] of Object.entries(timeSeriesData)) {\n  for (const [month, prices] of Object.entries(data.months)) {\n    const monthNum = month.substring(5, 7);\n    if (!seasonalPatterns[monthNum]) {\n      seasonalPatterns[monthNum] = [];\n    }\n    const avgPrice = prices.reduce((a, b) => a + b, 0) / prices.length;\n    seasonalPatterns[monthNum].push(avgPrice);\n  }\n}\n\nconst seasonalInsights = [];\nfor (const [month, prices] of Object.entries(seasonalPatterns)) {\n  const stats = calculateStats(prices);\n  seasonalInsights.push({\n    month: month,\n    avgPrice: stats.mean,\n    priceRange: { min: stats.min, max: stats.max },\n    volatility: stats.stdDev\n  });\n}\n\n// Return comprehensive statistical insights\nreturn [{\n  json: {\n    statisticalPatterns: {\n      byFlatTypeAndTown: statisticalInsights,\n      yearOverYearGrowth: yoyGrowth,\n      seasonalPatterns: seasonalInsights.sort((a, b) => a.month.localeCompare(b.month)),\n      overallTrends: {\n        totalRecordsAnalyzed: items.length,\n        yearsAnalyzed: years,\n        averageGrowthRate: yoyGrowth.length > 0 \n          ? Math.round((yoyGrowth.reduce((sum, g) => sum + g.growthRate, 0) / yoyGrowth.length) * 100) / 100 \n          : 0\n      }\n    },\n    analysisTimestamp: new Date().toISOString()\n  }\n}];"
          },
          "typeVersion": 2
        },
        {
          "id": "e873f7f2-1c48-491a-9b9d-fb70800670a5",
          "name": "Time Series Analysis",
          "type": "n8n-nodes-base.code",
          "position": [
            688,
            288
          ],
          "parameters": {
            "jsCode": "// Time Series Analysis for HDB Flat Prices\n// Calculates moving averages, momentum indicators, trends, cycles, volatility, and exponential smoothing\n\nconst items = $input.all();\n\n// Helper function to calculate moving average\nfunction calculateMovingAverage(data, period) {\n  const result = [];\n  for (let i = 0; i < data.length; i++) {\n    if (i < period - 1) {\n      result.push(null);\n    } else {\n      const sum = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);\n      result.push(sum / period);\n    }\n  }\n  return result;\n}\n\n// Helper function to calculate exponential moving average\nfunction calculateEMA(data, period) {\n  const k = 2 / (period + 1);\n  const ema = [data[0]];\n  \n  for (let i = 1; i < data.length; i++) {\n    ema.push(data[i] * k + ema[i - 1] * (1 - k));\n  }\n  return ema;\n}\n\n// Helper function to calculate standard deviation\nfunction calculateStdDev(data) {\n  const mean = data.reduce((a, b) => a + b, 0) / data.length;\n  const variance = data.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / data.length;\n  return Math.sqrt(variance);\n}\n\n// Sort items by date\nconst sortedItems = items.sort((a, b) => {\n  const dateA = new Date(a.json.month || a.json.date || a.json.year);\n  const dateB = new Date(b.json.month || b.json.date || b.json.year);\n  return dateA - dateB;\n});\n\n// Extract price data\nconst prices = sortedItems.map(item => parseFloat(item.json.resale_price || item.json.price || 0));\n\n// Calculate moving averages\nconst ma3 = calculateMovingAverage(prices, 3);\nconst ma6 = calculateMovingAverage(prices, 6);\nconst ma12 = calculateMovingAverage(prices, 12);\n\n// Calculate exponential moving averages\nconst ema3 = calculateEMA(prices, 3);\nconst ema6 = calculateEMA(prices, 6);\nconst ema12 = calculateEMA(prices, 12);\n\n// Calculate momentum indicators\nconst momentum = prices.map((price, i) => {\n  if (i < 3) return null;\n  return price - prices[i - 3];\n});\n\n// Calculate rate of change (ROC)\nconst roc = prices.map((price, i) => {\n  if (i < 12 || prices[i - 12] === 0) return null;\n  return ((price - prices[i - 12]) / prices[i - 12]) * 100;\n});\n\n// Identify trend direction\nconst recentPrices = prices.slice(-12);\nconst firstHalf = recentPrices.slice(0, 6).reduce((a, b) => a + b, 0) / 6;\nconst secondHalf = recentPrices.slice(6).reduce((a, b) => a + b, 0) / 6;\nconst trendDirection = secondHalf > firstHalf ? 'upward' : secondHalf < firstHalf ? 'downward' : 'stable';\nconst trendStrength = Math.abs(((secondHalf - firstHalf) / firstHalf) * 100);\n\n// Calculate volatility metrics\nconst returns = prices.slice(1).map((price, i) => (price - prices[i]) / prices[i]);\nconst volatility = calculateStdDev(returns) * 100;\n\n// Calculate rolling volatility (12-month window)\nconst rollingVolatility = [];\nfor (let i = 11; i < prices.length; i++) {\n  const windowPrices = prices.slice(i - 11, i + 1);\n  const windowReturns = windowPrices.slice(1).map((price, j) => (price - windowPrices[j]) / windowPrices[j]);\n  rollingVolatility.push(calculateStdDev(windowReturns) * 100);\n}\n\n// Detect cyclical patterns (simplified seasonal decomposition)\nconst monthlyAverages = {};\nsortedItems.forEach(item => {\n  const date = new Date(item.json.month || item.json.date || item.json.year);\n  const month = date.getMonth();\n  if (!monthlyAverages[month]) {\n    monthlyAverages[month] = [];\n  }\n  monthlyAverages[month].push(parseFloat(item.json.resale_price || item.json.price || 0));\n});\n\nconst seasonalFactors = {};\nfor (let month in monthlyAverages) {\n  seasonalFactors[month] = monthlyAverages[month].reduce((a, b) => a + b, 0) / monthlyAverages[month].length;\n}\n\nconst overallMean = prices.reduce((a, b) => a + b, 0) / prices.length;\nconst seasonalIndices = {};\nfor (let month in seasonalFactors) {\n  seasonalIndices[month] = (seasonalFactors[month] / overallMean) * 100;\n}\n\n// Prepare output with time series features\nconst timeSeriesFeatures = {\n  moving_averages: {\n    ma_3_month: ma3.filter(v => v !== null).slice(-1)[0] || null,\n    ma_6_month: ma6.filter(v => v !== null).slice(-1)[0] || null,\n    ma_12_month: ma12.filter(v => v !== null).slice(-1)[0] || null\n  },\n  exponential_moving_averages: {\n    ema_3_month: ema3.slice(-1)[0] || null,\n    ema_6_month: ema6.slice(-1)[0] || null,\n    ema_12_month: ema12.slice(-1)[0] || null\n  },\n  momentum_indicators: {\n    momentum_3_month: momentum.filter(v => v !== null).slice(-1)[0] || null,\n    rate_of_change_12_month: roc.filter(v => v !== null).slice(-1)[0] || null\n  },\n  trend_analysis: {\n    direction: trendDirection,\n    strength_percentage: trendStrength.toFixed(2),\n    current_price: prices.slice(-1)[0],\n    price_change_12_month: prices.length >= 12 ? prices.slice(-1)[0] - prices[prices.length - 12] : null\n  },\n  volatility_metrics: {\n    overall_volatility_percentage: volatility.toFixed(2),\n    recent_volatility_12_month: rollingVolatility.slice(-1)[0]?.toFixed(2) || null,\n    price_range: {\n      min: Math.min(...prices),\n      max: Math.max(...prices),\n      range: Math.max(...prices) - Math.min(...prices)\n    }\n  },\n  seasonal_patterns: {\n    seasonal_indices: seasonalIndices,\n    strongest_month: Object.keys(seasonalIndices).reduce((a, b) => seasonalIndices[a] > seasonalIndices[b] ? a : b),\n    weakest_month: Object.keys(seasonalIndices).reduce((a, b) => seasonalIndices[a] < seasonalIndices[b] ? a : b)\n  },\n  data_summary: {\n    total_observations: prices.length,\n    date_range: {\n      start: sortedItems[0].json.month || sortedItems[0].json.date || sortedItems[0].json.year,\n      end: sortedItems[sortedItems.length - 1].json.month || sortedItems[sortedItems.length - 1].json.date || sortedItems[sortedItems.length - 1].json.year\n    },\n    mean_price: overallMean.toFixed(2)\n  }\n};\n\nreturn [{ json: timeSeriesFeatures }];"
          },
          "typeVersion": 2
        },
        {
          "id": "c03ff069-3e4c-4ed1-9a5d-1544d06dc3bd",
          "name": "Aggregate Statistical Features",
          "type": "n8n-nodes-base.aggregate",
          "position": [
            912,
            192
          ],
          "parameters": {
            "options": {},
            "aggregate": "aggregateAllItemData",
            "destinationFieldName": "json"
          },
          "typeVersion": 1
        },
        {
          "id": "1b6af081-8b06-4196-ae79-79efffe84f54",
          "name": "AI Forecasting Agent",
          "type": "@n8n/n8n-nodes-langchain.agent",
          "position": [
            1232,
            80
          ],
          "parameters": {
            "text": "You are an expert real estate data scientist specializing in HDB flat price forecasting. Analyze the provided statistical patterns, time series features, and historical trends. Use the statistical_forecasting tool to generate probabilistic price predictions for the next 12 months. Consider seasonal patterns, market trends, location factors, and flat characteristics. Provide forecasts with confidence intervals for different flat types and towns.",
            "options": {},
            "promptType": "define",
            "hasOutputParser": true
          },
          "typeVersion": 3
        },
        {
          "id": "cfcbc04c-4f14-4473-b4e6-8c3d9a9389b3",
          "name": "OpenAI GPT-4 Model",
          "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
          "position": [
            1104,
            304
          ],
          "parameters": {
            "model": {
              "__rl": true,
              "mode": "id",
              "value": "gpt-4o"
            },
            "options": {}
          },
          "credentials": {
            "openAiApi": {
              "id": "credential-id",
              "name": "openAiApi Credential"
            }
          },
          "typeVersion": 1.2
        },
        {
          "id": "b31ccf03-1c0e-49e4-9419-a46f4767faf3",
          "name": "Statistical Forecasting Tool",
          "type": "@n8n/n8n-nodes-langchain.toolCode",
          "position": [
            1232,
            304
          ],
          "parameters": {
            "jsCode": "// Advanced Statistical Forecasting Tool for HDB Price Data\n// Implements linear regression, exponential smoothing, and Monte Carlo simulation\n\n// Parse input query - expecting JSON string with historical price data\nlet inputData;\ntry {\n  inputData = typeof query === 'string' ? JSON.parse(query) : query;\n} catch (e) {\n  return JSON.stringify({ error: 'Invalid input format. Expected JSON with historical price data.' });\n}\n\nconst prices = inputData.prices || [];\nconst dates = inputData.dates || [];\n\nif (prices.length < 3) {\n  return JSON.stringify({ error: 'Insufficient data points. Need at least 3 historical data points.' });\n}\n\n// Linear Regression Function\nfunction linearRegression(x, y) {\n  const n = x.length;\n  const sumX = x.reduce((a, b) => a + b, 0);\n  const sumY = y.reduce((a, b) => a + b, 0);\n  const sumXY = x.reduce((sum, xi, i) => sum + xi * y[i], 0);\n  const sumX2 = x.reduce((sum, xi) => sum + xi * xi, 0);\n  \n  const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);\n  const intercept = (sumY - slope * sumX) / n;\n  \n  return { slope, intercept };\n}\n\n// Calculate Mean Absolute Percentage Error\nfunction calculateMAPE(actual, predicted) {\n  const n = actual.length;\n  let sum = 0;\n  for (let i = 0; i < n; i++) {\n    sum += Math.abs((actual[i] - predicted[i]) / actual[i]);\n  }\n  return (sum / n) * 100;\n}\n\n// Exponential Smoothing\nfunction exponentialSmoothing(data, alpha = 0.3) {\n  const smoothed = [data[0]];\n  for (let i = 1; i < data.length; i++) {\n    smoothed.push(alpha * data[i] + (1 - alpha) * smoothed[i - 1]);\n  }\n  return smoothed;\n}\n\n// Monte Carlo Simulation for confidence intervals\nfunction monteCarloSimulation(basePrice, volatility, months, simulations = 1000) {\n  const results = [];\n  \n  for (let month = 1; month <= months; month++) {\n    const monthResults = [];\n    \n    for (let sim = 0; sim < simulations; sim++) {\n      // Random walk with drift\n      const randomShock = (Math.random() - 0.5) * 2 * volatility;\n      const price = basePrice * (1 + randomShock * Math.sqrt(month));\n      monthResults.push(price);\n    }\n    \n    monthResults.sort((a, b) => a - b);\n    results.push({\n      month: month,\n      mean: monthResults.reduce((a, b) => a + b, 0) / simulations,\n      lower: monthResults[Math.floor(simulations * 0.05)],\n      upper: monthResults[Math.floor(simulations * 0.95)],\n      median: monthResults[Math.floor(simulations * 0.5)]\n    });\n  }\n  \n  return results;\n}\n\n// Main forecasting logic\nconst timeIndices = prices.map((_, i) => i);\nconst { slope, intercept } = linearRegression(timeIndices, prices);\n\n// Calculate volatility (standard deviation of returns)\nconst returns = [];\nfor (let i = 1; i < prices.length; i++) {\n  returns.push((prices[i] - prices[i - 1]) / prices[i - 1]);\n}\nconst meanReturn = returns.reduce((a, b) => a + b, 0) / returns.length;\nconst variance = returns.reduce((sum, r) => sum + Math.pow(r - meanReturn, 2), 0) / returns.length;\nconst volatility = Math.sqrt(variance);\n\n// Apply exponential smoothing\nconst smoothedPrices = exponentialSmoothing(prices);\n\n// Generate predictions for next 12 months\nconst predictions = [];\nconst lastIndex = prices.length - 1;\nconst lastPrice = prices[lastIndex];\n\nfor (let month = 1; month <= 12; month++) {\n  const futureIndex = lastIndex + month;\n  const linearPrediction = slope * futureIndex + intercept;\n  const trendAdjusted = linearPrediction * (1 + meanReturn * month);\n  \n  predictions.push({\n    month: month,\n    predicted_price: Math.round(trendAdjusted),\n    linear_trend: Math.round(linearPrediction)\n  });\n}\n\n// Monte Carlo simulation for confidence intervals\nconst mcResults = monteCarloSimulation(lastPrice, volatility, 12);\n\n// Combine predictions with confidence intervals\nconst forecast = predictions.map((pred, i) => ({\n  month: pred.month,\n  predicted_price: pred.predicted_price,\n  lower_bound: Math.round(mcResults[i].lower),\n  upper_bound: Math.round(mcResults[i].upper),\n  confidence_level: '90%'\n}));\n\n// Calculate fitted values for accuracy metrics\nconst fittedValues = timeIndices.map(i => slope * i + intercept);\nconst mape = calculateMAPE(prices, fittedValues);\n\n// Prepare output\nconst output = {\n  forecast_12_months: forecast,\n  model_metrics: {\n    slope: slope.toFixed(4),\n    intercept: intercept.toFixed(2),\n    volatility: (volatility * 100).toFixed(2) + '%',\n    mape: mape.toFixed(2) + '%',\n    mean_return: (meanReturn * 100).toFixed(2) + '%'\n  },\n  summary: `Forecasted ${forecast.length} months ahead. Average predicted price: $${Math.round(forecast.reduce((sum, f) => sum + f.predicted_price, 0) / forecast.length)}. Model accuracy (MAPE): ${mape.toFixed(2)}%`\n};\n\nreturn JSON.stringify(output, null, 2);",
            "description": "Performs advanced statistical forecasting using linear regression, exponential smoothing, and trend analysis on HDB price data"
          },
          "typeVersion": 1.3
        },
        {
          "id": "1c0af53c-7990-4d68-9878-8ad6e76ba4a1",
          "name": "Calculator Tool",
          "type": "@n8n/n8n-nodes-langchain.toolCalculator",
          "position": [
            1360,
            304
          ],
          "parameters": {},
          "typeVersion": 1
        },
        {
          "id": "c35bd2a7-c691-4c94-8f35-f23d51d7b18b",
          "name": "Structured Forecast Output Parser",
          "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
          "position": [
            1488,
            304
          ],
          "parameters": {
            "schemaType": "manual",
            "inputSchema": "{\n  \"type\": \"object\",\n  \"properties\": {\n    \"flat_type\": {\n      \"type\": \"string\",\n      \"description\": \"Type of HDB flat (e.g., 3 ROOM, 4 ROOM, 5 ROOM, EXECUTIVE)\"\n    },\n    \"town\": {\n      \"type\": \"string\",\n      \"description\": \"Town or location of the HDB flat\"\n    },\n    \"forecast_month\": {\n      \"type\": \"string\",\n      \"description\": \"Month for which the forecast is made (e.g., 2024-01)\"\n    },\n    \"predicted_price\": {\n      \"type\": \"number\",\n      \"description\": \"Predicted resale price in SGD\"\n    },\n    \"lower_bound\": {\n      \"type\": \"number\",\n      \"description\": \"Lower bound of the price prediction confidence interval\"\n    },\n    \"upper_bound\": {\n      \"type\": \"number\",\n      \"description\": \"Upper bound of the price prediction confidence interval\"\n    },\n    \"confidence_level\": {\n      \"type\": \"number\",\n      \"description\": \"Confidence level of the prediction (0-1 scale)\"\n    },\n    \"trend_direction\": {\n      \"type\": \"string\",\n      \"description\": \"Direction of price trend (e.g., increasing, decreasing, stable)\"\n    },\n    \"key_factors\": {\n      \"type\": \"array\",\n      \"items\": {\n        \"type\": \"string\"\n      },\n      \"description\": \"Key factors influencing the forecast\"\n    }\n  },\n  \"required\": [\"flat_type\", \"town\", \"forecast_month\", \"predicted_price\", \"lower_bound\", \"upper_bound\", \"confidence_level\", \"trend_direction\", \"key_factors\"]\n}"
          },
          "typeVersion": 1.3
        },
        {
          "id": "24bf4ad3-a1cb-447a-8438-fab20e6392c3",
          "name": "Format Forecast Results",
          "type": "n8n-nodes-base.set",
          "position": [
            1728,
            80
          ],
          "parameters": {
            "options": {},
            "assignments": {
              "assignments": [
                {
                  "id": "id-1",
                  "name": "forecast_date",
                  "type": "string",
                  "value": "={{ $now.toISO() }}"
                },
                {
                  "id": "id-2",
                  "name": "model_version",
                  "type": "string",
                  "value": "v1.0"
                },
                {
                  "id": "id-3",
                  "name": "data_source",
                  "type": "string",
                  "value": "Singapore HDB Data.gov.sg"
                }
              ]
            },
            "includeOtherFields": true
          },
          "typeVersion": 3.4
        },
        {
          "id": "309d0df2-c531-40ae-ab2b-5dbf09969cb1",
          "name": "Save to Google Sheets",
          "type": "n8n-nodes-base.googleSheets",
          "position": [
            1920,
            80
          ],
          "parameters": {
            "columns": {
              "value": {
                "town": "={{ $json.town }}",
                "flat_type": "={{ $json.flat_type }}",
                "lower_bound": "={{ $json.lower_bound }}",
                "upper_bound": "={{ $json.upper_bound }}",
                "forecast_date": "={{ $json.forecast_date }}",
                "model_version": "={{ $json.model_version }}",
                "forecast_month": "={{ $json.forecast_month }}",
                "predicted_price": "={{ $json.predicted_price }}",
                "trend_direction": "={{ $json.trend_direction }}",
                "confidence_level": "={{ $json.confidence_level }}"
              },
              "schema": [
                {
                  "id": "flat_type",
                  "required": false,
                  "displayName": "flat_type",
                  "defaultMatch": true,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "town",
                  "required": false,
                  "displayName": "town",
                  "defaultMatch": true,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "forecast_month",
                  "required": false,
                  "displayName": "forecast_month",
                  "defaultMatch": true,
                  "canBeUsedToMatch": true
                },
                {
                  "id": "predicted_price",
                  "required": false,
                  "displayName": "predicted_price",
                  "defaultMatch": false,
                  "canBeUsedToMatch": false
                },
                {
                  "id": "lower_bound",
                  "required": false,
                  "displayName": "lower_bound",
                  "defaultMatch": false,
                  "canBeUsedToMatch": false
                },
                {
                  "id": "upper_bound",
                  "required": false,
                  "displayName": "upper_bound",
                  "defaultMatch": false,
                  "canBeUsedToMatch": false
                },
                {
                  "id": "confidence_level",
                  "required": false,
                  "displayName": "confidence_level",
                  "defaultMatch": false,
                  "canBeUsedToMatch": false
                },
                {
                  "id": "trend_direction",
                  "required": false,
                  "displayName": "trend_direction",
                  "defaultMatch": false,
                  "canBeUsedToMatch": false
                },
                {
                  "id": "forecast_date",
                  "required": false,
                  "displayName": "forecast_date",
                  "defaultMatch": false,
                  "canBeUsedToMatch": false
                },
                {
                  "id": "model_version",
                  "required": false,
                  "displayName": "model_version",
                  "defaultMatch": false,
                  "canBeUsedToMatch": false
                }
              ],
              "mappingMode": "defineBelow",
              "matchingColumns": [
                "flat_type",
                "town",
                "forecast_month"
              ]
            },
            "options": {},
            "operation": "appendOrUpdate",
            "sheetName": {
              "__rl": true,
              "mode": "name",
              "value": "HDB Price Forecasts"
            },
            "documentId": {
              "__rl": true,
              "mode": "id",
              "value": "<__PLACEHOLDER_VALUE__Google Sheets Document ID__>"
            }
          },
          "credentials": {
            "googleSheetsOAuth2Api": {
              "id": "credential-id",
              "name": "googleSheetsOAuth2Api Credential"
            }
          },
          "typeVersion": 4.7
        },
        {
          "id": "73fe488b-4fac-4105-94da-0a7c3bd51ee0",
          "name": "Sticky Note",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -496,
            -96
          ],
          "parameters": {
            "color": 7,
            "width": 864,
            "height": 848,
            "content": "## 1. Data Collection Layer\n\nFetches current-year HDB data with 3 years of historical records in parallel.\n**Why:** Eliminates manual gathering and provides full historical context for trend analysis."
          },
          "typeVersion": 1
        },
        {
          "id": "6da52b14-c464-4655-bcb3-5682edea4ba5",
          "name": "Sticky Note1",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            384,
            -96
          ],
          "parameters": {
            "color": 7,
            "width": 224,
            "height": 592,
            "content": "## 2. Data Consolidation & Cleaning\n\nMerges datasets and normalizes values.\n**Why:** Prevents fragmented data from corrupting analysis; clean, unified data is essential for accurate forecasts."
          },
          "typeVersion": 1
        },
        {
          "id": "0d843aeb-9906-414f-8f80-06e1a9a4d231",
          "name": "Sticky Note2",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            624,
            -96
          ],
          "parameters": {
            "color": 7,
            "width": 400,
            "height": 592,
            "content": "## 3. Pattern & Trend Analysis\n\nRuns statistical pattern mining and time series analysis on consolidated data.\n**Why:** Captures recurring patterns and long-term trends; dual approach avoids missing key signals."
          },
          "typeVersion": 1
        },
        {
          "id": "70857d8d-4dcd-4a21-8dba-32fd226af0f6",
          "name": "Sticky Note3",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            1056,
            -96
          ],
          "parameters": {
            "color": 7,
            "width": 592,
            "height": 592,
            "content": "## 4. AI Forecasting Agent (Intelligence Hub)\n\nProcesses features via GPT-4 with statistical forecasting and calculator tools.\n**Why:** Synthesizes multiple perspectives into actionable, validated predictions."
          },
          "typeVersion": 1
        },
        {
          "id": "f3e96eed-ef12-49fe-b3b2-a9f8f1ef6cc0",
          "name": "Sticky Note4",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            1680,
            -96
          ],
          "parameters": {
            "color": 7,
            "width": 448,
            "height": 384,
            "content": "## 5. Output & Storage\n\nFormats predictions and saves to Google Sheets.\n**Why:** Provides immediate access with an audit trail, no manual export needed."
          },
          "typeVersion": 1
        },
        {
          "id": "c5163331-6a18-4a32-8c9b-5ab6a780c3b9",
          "name": "Sticky Note5",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -496,
            -384
          ],
          "parameters": {
            "width": 592,
            "height": 256,
            "content": "## How It Works\nThe workflow runs on a monthly trigger to collect both current-year and multi-year historical HDB data. Once fetched, all datasets are merged with aligned fields to produce a unified table. The system then applies cleaning and normalization rules to ensure consistent scales and comparable values. After preprocessing, it performs pattern mining, anomaly checks, and time-series analysis to extract trends and forecast signals. An AI agent, integrating OpenAI GPT-4, statistical tools, and calculator nodes, synthesizes these results into coherent insights. The final predictions are formatted and automatically written to Google Sheets for reporting and downstream use.\n\n"
          },
          "typeVersion": 1
        },
        {
          "id": "88ef0bfa-84a8-49cb-a584-b7362e8b1fb1",
          "name": "Sticky Note6",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            128,
            -384
          ],
          "parameters": {
            "color": 2,
            "width": 624,
            "height": 256,
            "content": "## Setup Steps\n1) Configure fetch nodes to pull current-year HDB data and three years of historical records.\n2) Align and map column names across all datasets.\n3) Set normalization and standardization parameters in the cleaning node.\n4) Add your OpenAI API key (GPT-4) and link the model, forecasting tool, and calculator nodes.\n5) Authorize Google Sheets and configure sheet and cell mappings for automated export.\n \n"
          },
          "typeVersion": 1
        },
        {
          "id": "856eb6ed-d34d-4a25-b2f6-7536d2f51e14",
          "name": "Sticky Note7",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            784,
            -384
          ],
          "parameters": {
            "color": 3,
            "width": 496,
            "height": 144,
            "content": "## Prerequisites\n- Historical data source with API access (3+ years of records)\n- OpenAI API key for GPT-4 model\n- Google Sheets account with API credentials\n- Basic understanding of time series data\n"
          },
          "typeVersion": 1
        },
        {
          "id": "a4b2ea8f-8349-477c-b204-ae061ac371d6",
          "name": "Sticky Note8",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            1312,
            -384
          ],
          "parameters": {
            "color": 4,
            "width": 256,
            "height": 224,
            "content": "\n## Use Cases\n**Real Estate:** Forecast property prices using multi-year historical HDB/market data with confidence intervals\n**Finance:** Predict market trends by aggregating years of transaction or pricing records\n"
          },
          "typeVersion": 1
        },
        {
          "id": "4be19165-e2d7-4a95-903e-7fbd55d71995",
          "name": "Sticky Note9",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            1600,
            -384
          ],
          "parameters": {
            "color": 6,
            "width": 464,
            "height": 256,
            "content": "\n## Customization\n**Data Source:** Replace HDB/fetch nodes with stock prices, sensor data, sales records, or any historical dataset\n**Analysis Window:** Adjust years fetched (2-5 years) based on data availability and prediction horizon\n\n\n## Benefits\n**Automation:** Monthly scheduling eliminates manual data gathering and analysis\n "
          },
          "typeVersion": 1
        }
      ],
      "active": false,
      "pinData": {},
      "settings": {
        "executionOrder": "v1"
      },
      "versionId": "3ea99275-1c33-4516-80c8-eb3bcd4fe06c",
      "connections": {
        "Calculator Tool": {
          "ai_tool": [
            [
              {
                "node": "AI Forecasting Agent",
                "type": "ai_tool",
                "index": 0
              }
            ]
          ]
        },
        "OpenAI GPT-4 Model": {
          "ai_languageModel": [
            [
              {
                "node": "AI Forecasting Agent",
                "type": "ai_languageModel",
                "index": 0
              }
            ]
          ]
        },
        "AI Forecasting Agent": {
          "main": [
            [
              {
                "node": "Format Forecast Results",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Time Series Analysis": {
          "main": [
            [
              {
                "node": "Aggregate Statistical Features",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Workflow Configuration": {
          "main": [
            [
              {
                "node": "Fetch Current Year HDB Data",
                "type": "main",
                "index": 0
              },
              {
                "node": "Fetch Historical Data Year 1",
                "type": "main",
                "index": 0
              },
              {
                "node": "Fetch Historical Data Year 2",
                "type": "main",
                "index": 0
              },
              {
                "node": "Fetch Historical Data Year 3",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Forecast Results": {
          "main": [
            [
              {
                "node": "Save to Google Sheets",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Merge All Historical Data": {
          "main": [
            [
              {
                "node": "Data Cleaning and Normalization",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Statistical Pattern Mining": {
          "main": [
            [
              {
                "node": "Aggregate Statistical Features",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Fetch Current Year HDB Data": {
          "main": [
            [
              {
                "node": "Merge All Historical Data",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Fetch Historical Data Year 1": {
          "main": [
            [
              {
                "node": "Merge All Historical Data",
                "type": "main",
                "index": 1
              }
            ]
          ]
        },
        "Fetch Historical Data Year 2": {
          "main": [
            [
              {
                "node": "Merge All Historical Data",
                "type": "main",
                "index": 2
              }
            ]
          ]
        },
        "Fetch Historical Data Year 3": {
          "main": [
            [
              {
                "node": "Merge All Historical Data",
                "type": "main",
                "index": 3
              }
            ]
          ]
        },
        "Statistical Forecasting Tool": {
          "ai_tool": [
            [
              {
                "node": "AI Forecasting Agent",
                "type": "ai_tool",
                "index": 0
              }
            ]
          ]
        },
        "Aggregate Statistical Features": {
          "main": [
            [
              {
                "node": "AI Forecasting Agent",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Data Cleaning and Normalization": {
          "main": [
            [
              {
                "node": "Statistical Pattern Mining",
                "type": "main",
                "index": 0
              },
              {
                "node": "Time Series Analysis",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Monthly Data Collection Trigger": {
          "main": [
            [
              {
                "node": "Workflow Configuration",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Structured Forecast Output Parser": {
          "ai_outputParser": [
            [
              {
                "node": "AI Forecasting Agent",
                "type": "ai_outputParser",
                "index": 0
              }
            ]
          ]
        }
      }
    },
    "lastUpdatedBy": 1,
    "workflowInfo": {
      "nodeCount": 28,
      "nodeTypes": {
        "n8n-nodes-base.set": {
          "count": 2
        },
        "n8n-nodes-base.code": {
          "count": 3
        },
        "n8n-nodes-base.merge": {
          "count": 1
        },
        "n8n-nodes-base.aggregate": {
          "count": 1
        },
        "n8n-nodes-base.stickyNote": {
          "count": 10
        },
        "n8n-nodes-base.httpRequest": {
          "count": 4
        },
        "n8n-nodes-base.googleSheets": {
          "count": 1
        },
        "@n8n/n8n-nodes-langchain.agent": {
          "count": 1
        },
        "n8n-nodes-base.scheduleTrigger": {
          "count": 1
        },
        "@n8n/n8n-nodes-langchain.toolCode": {
          "count": 1
        },
        "@n8n/n8n-nodes-langchain.lmChatOpenAi": {
          "count": 1
        },
        "@n8n/n8n-nodes-langchain.toolCalculator": {
          "count": 1
        },
        "@n8n/n8n-nodes-langchain.outputParserStructured": {
          "count": 1
        }
      }
    },
    "status": "published",
    "user": {
      "name": "Cheng Siong Chin",
      "username": "cschin",
      "bio": "Dr. Cheng Siong CHIN is an n8n workflow creator specializing in AI-powered automation, agent orchestration, and intelligent system integrations. He designs and builds end-to-end workflows that combine LLMs, APIs, and data pipelines to streamline complex processes and deliver production-ready automation solutions. Contact me to discuss custom AI workflows and agent architectures.\n",
      "verified": true,
      "links": [
        "https://gravatar.com/mysticluminary9fa255f7f5"
      ],
      "avatar": "https://gravatar.com/avatar/54544f98e839bb9dd9a764ad1e6823eeddb6db5138d201e42f291a7b0a73303f?r=pg&d=retro&size=200"
    },
    "nodes": [
      {
        "id": 18,
        "icon": "file:googleSheets.svg",
        "name": "n8n-nodes-base.googleSheets",
        "codex": {
          "data": {
            "alias": [
              "CSV",
              "Sheet",
              "Spreadsheet",
              "GS"
            ],
            "resources": {
              "generic": [
                {
                  "url": "https://n8n.io/blog/love-at-first-sight-ricardos-n8n-journey/",
                  "icon": "❤️",
                  "label": "Love at first sight: Ricardo’s n8n journey"
                },
                {
                  "url": "https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/",
                  "icon": "🧬",
                  "label": "Why business process automation with n8n can change your daily life"
                },
                {
                  "url": "https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/",
                  "icon": "🧾",
                  "label": "Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"
                },
                {
                  "url": "https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/",
                  "icon": "🎫",
                  "label": "Supercharging your conference registration process with n8n"
                },
                {
                  "url": "https://n8n.io/blog/creating-triggers-for-n8n-workflows-using-polling/",
                  "icon": "⏲",
                  "label": "Creating triggers for n8n workflows using polling"
                },
                {
                  "url": "https://n8n.io/blog/no-code-ecommerce-workflow-automations/",
                  "icon": "store",
                  "label": "6 e-commerce workflows to power up your Shopify s"
                },
                {
                  "url": "https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/",
                  "icon": "📈",
                  "label": "Migrating Community Metrics to Orbit using n8n"
                },
                {
                  "url": "https://n8n.io/blog/automate-google-apps-for-productivity/",
                  "icon": "💡",
                  "label": "15 Google apps you can combine and automate to increase productivity"
                },
                {
                  "url": "https://n8n.io/blog/your-business-doesnt-need-you-to-operate/",
                  "icon": " 🖥️",
                  "label": "Hey founders! Your business doesn't need you to operate"
                },
                {
                  "url": "https://n8n.io/blog/how-honest-burgers-use-automation-to-save-100k-per-year/",
                  "icon": "🍔",
                  "label": "How Honest Burgers Use Automation to Save $100k per year"
                },
                {
                  "url": "https://n8n.io/blog/how-a-digital-strategist-uses-n8n-for-online-marketing/",
                  "icon": "💻",
                  "label": "How a digital strategist uses n8n for online marketing"
                },
                {
                  "url": "https://n8n.io/blog/why-this-product-manager-loves-workflow-automation-with-n8n/",
                  "icon": "🧠",
                  "label": "Why this Product Manager loves workflow automation with n8n"
                },
                {
                  "url": "https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/",
                  "icon": "🙌",
                  "label": "Sending Automated Congratulations with Google Sheets, Twilio, and n8n "
                },
                {
                  "url": "https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/",
                  "icon": "📈",
                  "label": "How a Membership Development Manager automates his work and investments"
                },
                {
                  "url": "https://n8n.io/blog/aws-workflow-automation/",
                  "label": "7 no-code workflow automations for Amazon Web Services"
                }
              ],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets/"
                }
              ],
              "credentialDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/credentials/google/oauth-single-service/"
                }
              ]
            },
            "categories": [
              "Data & Storage",
              "Productivity"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0"
          }
        },
        "group": "[\"input\",\"output\"]",
        "defaults": {
          "name": "Google Sheets"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2MCIgaGVpZ2h0PSI2MCI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGZpbGw9IiMyOEI0NDYiIGQ9Ik0zNS42OSAxIDUyIDE3LjIyNXYzOS4wODdhMy42NyAzLjY3IDAgMCAxLTEuMDg0IDIuNjFBMy43IDMuNyAwIDAgMSA0OC4yOTMgNjBIMTIuNzA3YTMuNyAzLjcgMCAwIDEtMi42MjMtMS4wNzhBMy42NyAzLjY3IDAgMCAxIDkgNTYuMzEyVjQuNjg4YTMuNjcgMy42NyAwIDAgMSAxLjA4NC0yLjYxQTMuNyAzLjcgMCAwIDEgMTIuNzA3IDF6Ii8+PHBhdGggZmlsbD0iIzZBQ0U3QyIgZD0iTTM1LjY5IDEgNTIgMTcuMjI1SDM5LjM5N2MtMi4wNTQgMC0zLjcwNy0xLjgyOS0zLjcwNy0zLjg3MnoiLz48cGF0aCBmaWxsPSIjMjE5QjM4IiBkPSJNMzkuMjExIDE3LjIyNSA1MiAyMi40OHYtNS4yNTV6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZD0iTTIwLjEyIDMxLjk3NWMwLS44MTcuNjYyLTEuNDc1IDEuNDgzLTEuNDc1aDE3Ljc5NGMuODIxIDAgMS40ODIuNjU4IDEuNDgyIDEuNDc1djE1LjQ4N2MwIC44MTgtLjY2MSAxLjQ3NS0xLjQ4MiAxLjQ3NUgyMS42MDNhMS40NzYgMS40NzYgMCAwIDEtMS40ODItMS40NzRWMzEuOTc0em0yLjIyNSAxLjQ3NWg2LjY3MnYyLjIxMmgtNi42NzJ6bTAgNS4xNjJoNi42NzJ2Mi4yMTNoLTYuNjcyem0wIDUuMTYzaDYuNjcydjIuMjEyaC02LjY3MnptOS42MzgtMTAuMzI1aDYuNjcydjIuMjEyaC02LjY3MnptMCA1LjE2Mmg2LjY3MnYyLjIxM2gtNi42NzJ6bTAgNS4xNjNoNi42NzJ2Mi4yMTJoLTYuNjcyeiIvPjxwYXRoIGZpbGw9IiMyOEI0NDYiIGQ9Ik0zNC42OSAwIDUxIDE2LjIyNXYzOS4wODdhMy42NyAzLjY3IDAgMCAxLTEuMDg0IDIuNjFBMy43IDMuNyAwIDAgMSA0Ny4yOTMgNTlIMTEuNzA3YTMuNyAzLjcgMCAwIDEtMi42MjMtMS4wNzhBMy42NyAzLjY3IDAgMCAxIDggNTUuMzEyVjMuNjg4YTMuNjcgMy42NyAwIDAgMSAxLjA4NC0yLjYxQTMuNyAzLjcgMCAwIDEgMTEuNzA3IDB6Ii8+PHBhdGggZmlsbD0iIzZBQ0U3QyIgZD0iTTM0LjY5IDAgNTEgMTYuMjI1SDM4LjM5N2MtMi4wNTQgMC0zLjcwNy0xLjgyOS0zLjcwNy0zLjg3MnoiLz48cGF0aCBmaWxsPSIjMjE5QjM4IiBkPSJNMzguMjExIDE2LjIyNSA1MSAyMS40OHYtNS4yNTV6Ii8+PHBhdGggZmlsbD0iI0ZGRiIgZD0iTTE5LjEyIDMwLjk3NWMwLS44MTcuNjYyLTEuNDc1IDEuNDgzLTEuNDc1aDE3Ljc5NGMuODIxIDAgMS40ODIuNjU4IDEuNDgyIDEuNDc1djE1LjQ4N2MwIC44MTgtLjY2MSAxLjQ3NS0xLjQ4MiAxLjQ3NUgyMC42MDNhMS40NzYgMS40NzYgMCAwIDEtMS40ODItMS40NzRWMzAuOTc0em0yLjIyNSAxLjQ3NWg2LjY3MnYyLjIxMmgtNi42NzJ6bTAgNS4xNjJoNi42NzJ2Mi4yMTNoLTYuNjcyem0wIDUuMTYzaDYuNjcydjIuMjEyaC02LjY3MnptOS42MzgtMTAuMzI1aDYuNjcydjIuMjEyaC02LjY3MnptMCA1LjE2Mmg2LjY3MnYyLjIxM2gtNi42NzJ6bTAgNS4xNjNoNi42NzJ2Mi4yMTJoLTYuNjcyeiIvPjwvZz48L3N2Zz4="
        },
        "displayName": "Google Sheets",
        "typeVersion": 5,
        "nodeCategories": [
          {
            "id": 3,
            "name": "Data & Storage"
          },
          {
            "id": 4,
            "name": "Productivity"
          }
        ]
      },
      {
        "id": 19,
        "icon": "file:httprequest.svg",
        "name": "n8n-nodes-base.httpRequest",
        "codex": {
          "data": {
            "alias": [
              "API",
              "Request",
              "URL",
              "Build",
              "cURL"
            ],
            "resources": {
              "generic": [
                {
                  "url": "https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/",
                  "icon": "☀️",
                  "label": "2021: The Year to Automate the New You with n8n"
                },
                {
                  "url": "https://n8n.io/blog/why-business-process-automation-with-n8n-can-change-your-daily-life/",
                  "icon": "🧬",
                  "label": "Why business process automation with n8n can change your daily life"
                },
                {
                  "url": "https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/",
                  "icon": "📈",
                  "label": "Automatically pulling and visualizing data with n8n"
                },
                {
                  "url": "https://n8n.io/blog/learn-how-to-automatically-cross-post-your-content-with-n8n/",
                  "icon": "✍️",
                  "label": "Learn how to automatically cross-post your content with n8n"
                },
                {
                  "url": "https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/",
                  "icon": "🧾",
                  "label": "Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"
                },
                {
                  "url": "https://n8n.io/blog/running-n8n-on-ships-an-interview-with-maranics/",
                  "icon": "🛳",
                  "label": "Running n8n on ships: An interview with Maranics"
                },
                {
                  "url": "https://n8n.io/blog/what-are-apis-how-to-use-them-with-no-code/",
                  "icon": " 🪢",
                  "label": "What are APIs and how to use them with no code"
                },
                {
                  "url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/",
                  "icon": "⚡️",
                  "label": "5 tasks you can automate with the new Notion API "
                },
                {
                  "url": "https://n8n.io/blog/world-poetry-day-workflow/",
                  "icon": "📜",
                  "label": "Celebrating World Poetry Day with a daily poem in Telegram"
                },
                {
                  "url": "https://n8n.io/blog/automate-google-apps-for-productivity/",
                  "icon": "💡",
                  "label": "15 Google apps you can combine and automate to increase productivity"
                },
                {
                  "url": "https://n8n.io/blog/automate-designs-with-bannerbear-and-n8n/",
                  "icon": "🎨",
                  "label": "Automate Designs with Bannerbear and n8n"
                },
                {
                  "url": "https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/",
                  "icon": " 🕸️",
                  "label": "How uProc scraped a multi-page website with a low-code workflow"
                },
                {
                  "url": "https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/",
                  "icon": "📱",
                  "label": "Building an expense tracking app in 10 minutes"
                },
                {
                  "url": "https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/",
                  "icon": "🤖",
                  "label": "5 workflow automations for Mattermost that we love at n8n"
                },
                {
                  "url": "https://n8n.io/blog/how-to-use-the-http-request-node-the-swiss-army-knife-for-workflow-automation/",
                  "icon": "🧰",
                  "label": "How to use the HTTP Request Node - The Swiss Army Knife for Workflow Automation"
                },
                {
                  "url": "https://n8n.io/blog/learn-how-to-use-webhooks-with-mattermost-slash-commands/",
                  "icon": "🦄",
                  "label": "Learn how to use webhooks with Mattermost slash commands"
                },
                {
                  "url": "https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/",
                  "icon": "📈",
                  "label": "How a Membership Development Manager automates his work and investments"
                },
                {
                  "url": "https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/",
                  "icon": "📈",
                  "label": "A low-code bitcoin ticker built with QuestDB and n8n.io"
                },
                {
                  "url": "https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/",
                  "icon": "🎡",
                  "label": "How to set up a no-code CI/CD pipeline with GitHub and TravisCI"
                },
                {
                  "url": "https://n8n.io/blog/automations-for-activists/",
                  "icon": "✨",
                  "label": "How Common Knowledge use workflow automation for activism"
                },
                {
                  "url": "https://n8n.io/blog/creating-scheduled-text-affirmations-with-n8n/",
                  "icon": "🤟",
                  "label": "Creating scheduled text affirmations with n8n"
                },
                {
                  "url": "https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/",
                  "icon": "🛵",
                  "label": "How Goomer automated their operations with over 200 n8n workflows"
                },
                {
                  "url": "https://n8n.io/blog/aws-workflow-automation/",
                  "label": "7 no-code workflow automations for Amazon Web Services"
                }
              ],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/"
                }
              ]
            },
            "categories": [
              "Development",
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Helpers"
              ]
            }
          }
        },
        "group": "[\"output\"]",
        "defaults": {
          "name": "HTTP Request",
          "color": "#0004F5"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MCAyMEM0MCA4Ljk1MzE0IDMxLjA0NjkgMCAyMCAwQzguOTUzMTQgMCAwIDguOTUzMTQgMCAyMEMwIDMxLjA0NjkgOC45NTMxNCA0MCAyMCA0MEMzMS4wNDY5IDQwIDQwIDMxLjA0NjkgNDAgMjBaTTIwIDM2Ljk0NThDMTguODg1MiAzNi45NDU4IDE3LjEzNzggMzUuOTY3IDE1LjQ5OTggMzIuNjk4NUMxNC43OTY0IDMxLjI5MTggMTQuMTk2MSAyOS41NDMxIDEzLjc1MjYgMjcuNjg0N0gyNi4xODk4QzI1LjgwNDUgMjkuNTQwMyAyNS4yMDQ0IDMxLjI5MDEgMjQuNTAwMiAzMi42OTg1QzIyLjg2MjIgMzUuOTY3IDIxLjExNDggMzYuOTQ1OCAyMCAzNi45NDU4Wk0xMi45MDY0IDIwQzEyLjkwNjQgMjEuNjA5NyAxMy4wMDg3IDIzLjE2NCAxMy4yMDAzIDI0LjYzMDVIMjYuNzk5N0MyNi45OTEzIDIzLjE2NCAyNy4wOTM2IDIxLjYwOTcgMjcuMDkzNiAyMEMyNy4wOTM2IDE4LjM5MDMgMjYuOTkxMyAxNi44MzYgMjYuNzk5NyAxNS4zNjk1SDEzLjIwMDNDMTMuMDA4NyAxNi44MzYgMTIuOTA2NCAxOC4zOTAzIDEyLjkwNjQgMjBaTTIwIDMuMDU0MTlDMjEuMTE0OSAzLjA1NDE5IDIyLjg2MjIgNC4wMzA3OCAyNC41MDAxIDcuMzAwMzlDMjUuMjA2NiA4LjcxNDA4IDI1LjgwNzIgMTAuNDA2NyAyNi4xOTIgMTIuMzE1M0gxMy43NTAxQzE0LjE5MzMgMTAuNDA0NyAxNC43OTQyIDguNzEyNTQgMTUuNDk5OCA3LjMwMDY0QzE3LjEzNzcgNC4wMzA4MyAxOC44ODUxIDMuMDU0MTkgMjAgMy4wNTQxOVpNMzAuMTQ3OCAyMEMzMC4xNDc4IDE4LjQwOTkgMzAuMDU0MyAxNi44NjE3IDI5LjgyMjcgMTUuMzY5NUgzNi4zMDQyQzM2LjcyNTIgMTYuODQyIDM2Ljk0NTggMTguMzk2NCAzNi45NDU4IDIwQzM2Ljk0NTggMjEuNjAzNiAzNi43MjUyIDIzLjE1OCAzNi4zMDQyIDI0LjYzMDVIMjkuODIyN0MzMC4wNTQzIDIzLjEzODMgMzAuMTQ3OCAyMS41OTAxIDMwLjE0NzggMjBaTTI2LjI3NjcgNC4yNTUxMkMyNy42MzY1IDYuMzYwMTkgMjguNzExIDkuMTMyIDI5LjM3NzQgMTIuMzE1M0gzNS4xMDQ2QzMzLjI1MTEgOC42NjggMzAuMTA3IDUuNzgzNDYgMjYuMjc2NyA0LjI1NTEyWk0xMC42MjI2IDEyLjMxNTNINC44OTI5M0M2Ljc1MTQ3IDguNjY3ODQgOS44OTM1MSA1Ljc4MzQxIDEzLjcyMzIgNC4yNTUxM0MxMi4zNjM1IDYuMzYwMjEgMTEuMjg5IDkuMTMyMDEgMTAuNjIyNiAxMi4zMTUzWk0zLjA1NDE5IDIwQzMuMDU0MTkgMjEuNjAzIDMuMjc3NDMgMjMuMTU3NSAzLjY5NDg0IDI0LjYzMDVIMTAuMTIxN0M5Ljk0NjE5IDIzLjE0MiA5Ljg1MjIyIDIxLjU5NDMgOS44NTIyMiAyMEM5Ljg1MjIyIDE4LjQwNTcgOS45NDYxOSAxNi44NTggMTAuMTIxNyAxNS4zNjk1SDMuNjk0ODRDMy4yNzc0MyAxNi44NDI1IDMuMDU0MTkgMTguMzk3IDMuMDU0MTkgMjBaTTI2LjI3NjYgMzUuNzQyN0MyNy42MzY1IDMzLjYzOTMgMjguNzExIDMwLjg2OCAyOS4zNzc0IDI3LjY4NDdIMzUuMTA0NkMzMy4yNTEgMzEuMzMyMiAzMC4xMDY4IDM0LjIxNzkgMjYuMjc2NiAzNS43NDI3Wk0xMy43MjM0IDM1Ljc0MjdDOS44OTM2OSAzNC4yMTc5IDYuNzUxNTUgMzEuMzMyNCA0Ljg5MjkzIDI3LjY4NDdIMTAuNjIyNkMxMS4yODkgMzAuODY4IDEyLjM2MzUgMzMuNjM5MyAxMy43MjM0IDM1Ljc0MjdaIiBmaWxsPSIjM0E0MkU5Ii8+Cjwvc3ZnPgo="
        },
        "displayName": "HTTP Request",
        "typeVersion": 4,
        "nodeCategories": [
          {
            "id": 5,
            "name": "Development"
          },
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 24,
        "icon": "file:merge.svg",
        "name": "n8n-nodes-base.merge",
        "codex": {
          "data": {
            "alias": [
              "Join",
              "Concatenate",
              "Wait"
            ],
            "resources": {
              "generic": [
                {
                  "url": "https://n8n.io/blog/how-to-sync-data-between-two-systems/",
                  "icon": "🏬",
                  "label": "How to synchronize data between two systems (one-way vs. two-way sync"
                },
                {
                  "url": "https://n8n.io/blog/supercharging-your-conference-registration-process-with-n8n/",
                  "icon": "🎫",
                  "label": "Supercharging your conference registration process with n8n"
                },
                {
                  "url": "https://n8n.io/blog/migrating-community-metrics-to-orbit-using-n8n/",
                  "icon": "📈",
                  "label": "Migrating Community Metrics to Orbit using n8n"
                },
                {
                  "url": "https://n8n.io/blog/build-your-own-virtual-assistant-with-n8n-a-step-by-step-guide/",
                  "icon": "👦",
                  "label": "Build your own virtual assistant with n8n: A step by step guide"
                },
                {
                  "url": "https://n8n.io/blog/sending-automated-congratulations-with-google-sheets-twilio-and-n8n/",
                  "icon": "🙌",
                  "label": "Sending Automated Congratulations with Google Sheets, Twilio, and n8n "
                },
                {
                  "url": "https://n8n.io/blog/aws-workflow-automation/",
                  "label": "7 no-code workflow automations for Amazon Web Services"
                }
              ],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.merge/"
                }
              ]
            },
            "categories": [
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Flow",
                "Data Transformation"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "Merge"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTc3XzUxOCkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTAgNDhDMCAyMS40OTAzIDIxLjQ5MDMgMCA0OCAwSDExMkMxMzguNTEgMCAxNjAgMjEuNDkwMyAxNjAgNDhWNTZIMTk2LjI1MkMyNDAuNDM1IDU2IDI3Ni4yNTIgOTEuODE3MiAyNzYuMjUyIDEzNlYxOTJDMjc2LjI1MiAyMTQuMDkxIDI5NC4xNjEgMjMyIDMxNi4yNTIgMjMySDM1MlYyMjRDMzUyIDE5Ny40OSAzNzMuNDkgMTc2IDQwMCAxNzZINDY0QzQ5MC41MSAxNzYgNTEyIDE5Ny40OSA1MTIgMjI0VjI4OEM1MTIgMzE0LjUxIDQ5MC41MSAzMzYgNDY0IDMzNkg0MDBDMzczLjQ5IDMzNiAzNTIgMzE0LjUxIDM1MiAyODhWMjgwSDMxNi4yNTJDMjk0LjE2MSAyODAgMjc2LjI1MiAyOTcuOTA5IDI3Ni4yNTIgMzIwVjM3NkMyNzYuMjUyIDQyMC4xODMgMjQwLjQzNSA0NTYgMTk2LjI1MiA0NTZIMTYwVjQ2NEMxNjAgNDkwLjUxIDEzOC41MSA1MTIgMTEyIDUxMkg0OEMyMS40OTAzIDUxMiAwIDQ5MC41MSAwIDQ2NFY0MDBDMCAzNzMuNDkgMjEuNDkwMyAzNTIgNDggMzUySDExMkMxMzguNTEgMzUyIDE2MCAzNzMuNDkgMTYwIDQwMFY0MDhIMTk2LjI1MkMyMTMuOTI1IDQwOCAyMjguMjUyIDM5My42NzMgMjI4LjI1MiAzNzZWMzIwQzIyOC4yNTIgMjk0Ljc4NCAyMzguODU5IDI3Mi4wNDQgMjU1Ljg1MyAyNTZDMjM4Ljg1OSAyMzkuOTU2IDIyOC4yNTIgMjE3LjIxNiAyMjguMjUyIDE5MlYxMzZDMjI4LjI1MiAxMTguMzI3IDIxMy45MjUgMTA0IDE5Ni4yNTIgMTA0SDE2MFYxMTJDMTYwIDEzOC41MSAxMzguNTEgMTYwIDExMiAxNjBINDhDMjEuNDkwMyAxNjAgMCAxMzguNTEgMCAxMTJWNDhaTTEwNCA0OEMxMDguNDE4IDQ4IDExMiA1MS41ODE3IDExMiA1NlYxMDRDMTEyIDEwOC40MTggMTA4LjQxOCAxMTIgMTA0IDExMkg1NkM1MS41ODE3IDExMiA0OCAxMDguNDE4IDQ4IDEwNFY1NkM0OCA1MS41ODE3IDUxLjU4MTcgNDggNTYgNDhIMTA0Wk00NTYgMjI0QzQ2MC40MTggMjI0IDQ2NCAyMjcuNTgyIDQ2NCAyMzJWMjgwQzQ2NCAyODQuNDE4IDQ2MC40MTggMjg4IDQ1NiAyODhINDA4QzQwMy41ODIgMjg4IDQwMCAyODQuNDE4IDQwMCAyODBWMjMyQzQwMCAyMjcuNTgyIDQwMy41ODIgMjI0IDQwOCAyMjRINDU2Wk0xMTIgNDA4QzExMiA0MDMuNTgyIDEwOC40MTggNDAwIDEwNCA0MDBINTZDNTEuNTgxNyA0MDAgNDggNDAzLjU4MiA0OCA0MDhWNDU2QzQ4IDQ2MC40MTggNTEuNTgxNyA0NjQgNTYgNDY0SDEwNEMxMDguNDE4IDQ2NCAxMTIgNDYwLjQxOCAxMTIgNDU2VjQwOFoiIGZpbGw9IiM1NEI4QzkiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTc3XzUxOCI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="
        },
        "displayName": "Merge",
        "typeVersion": 3,
        "nodeCategories": [
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 38,
        "icon": "fa:pen",
        "name": "n8n-nodes-base.set",
        "codex": {
          "data": {
            "alias": [
              "Set",
              "JS",
              "JSON",
              "Filter",
              "Transform",
              "Map"
            ],
            "resources": {
              "generic": [
                {
                  "url": "https://n8n.io/blog/learn-to-automate-your-factorys-incident-reporting-a-step-by-step-guide/",
                  "icon": "🏭",
                  "label": "Learn to Automate Your Factory's Incident Reporting: A Step by Step Guide"
                },
                {
                  "url": "https://n8n.io/blog/2021-the-year-to-automate-the-new-you-with-n8n/",
                  "icon": "☀️",
                  "label": "2021: The Year to Automate the New You with n8n"
                },
                {
                  "url": "https://n8n.io/blog/automatically-pulling-and-visualizing-data-with-n8n/",
                  "icon": "📈",
                  "label": "Automatically pulling and visualizing data with n8n"
                },
                {
                  "url": "https://n8n.io/blog/database-monitoring-and-alerting-with-n8n/",
                  "icon": "📡",
                  "label": "Database Monitoring and Alerting with n8n"
                },
                {
                  "url": "https://n8n.io/blog/automatically-adding-expense-receipts-to-google-sheets-with-telegram-mindee-twilio-and-n8n/",
                  "icon": "🧾",
                  "label": "Automatically Adding Expense Receipts to Google Sheets with Telegram, Mindee, Twilio, and n8n"
                },
                {
                  "url": "https://n8n.io/blog/no-code-ecommerce-workflow-automations/",
                  "icon": "store",
                  "label": "6 e-commerce workflows to power up your Shopify s"
                },
                {
                  "url": "https://n8n.io/blog/how-to-build-a-low-code-self-hosted-url-shortener/",
                  "icon": "🔗",
                  "label": "How to build a low-code, self-hosted URL shortener in 3 steps"
                },
                {
                  "url": "https://n8n.io/blog/automate-your-data-processing-pipeline-in-9-steps-with-n8n/",
                  "icon": "⚙️",
                  "label": "Automate your data processing pipeline in 9 steps"
                },
                {
                  "url": "https://n8n.io/blog/how-to-get-started-with-crm-automation-and-no-code-workflow-ideas/",
                  "icon": "👥",
                  "label": "How to get started with CRM automation (with 3 no-code workflow ideas"
                },
                {
                  "url": "https://n8n.io/blog/5-tasks-you-can-automate-with-notion-api/",
                  "icon": "⚡️",
                  "label": "5 tasks you can automate with the new Notion API "
                },
                {
                  "url": "https://n8n.io/blog/automate-google-apps-for-productivity/",
                  "icon": "💡",
                  "label": "15 Google apps you can combine and automate to increase productivity"
                },
                {
                  "url": "https://n8n.io/blog/how-uproc-scraped-a-multi-page-website-with-a-low-code-workflow/",
                  "icon": " 🕸️",
                  "label": "How uProc scraped a multi-page website with a low-code workflow"
                },
                {
                  "url": "https://n8n.io/blog/building-an-expense-tracking-app-in-10-minutes/",
                  "icon": "📱",
                  "label": "Building an expense tracking app in 10 minutes"
                },
                {
                  "url": "https://n8n.io/blog/the-ultimate-guide-to-automate-your-video-collaboration-with-whereby-mattermost-and-n8n/",
                  "icon": "📹",
                  "label": "The ultimate guide to automate your video collaboration with Whereby, Mattermost, and n8n"
                },
                {
                  "url": "https://n8n.io/blog/5-workflow-automations-for-mattermost-that-we-love-at-n8n/",
                  "icon": "🤖",
                  "label": "5 workflow automations for Mattermost that we love at n8n"
                },
                {
                  "url": "https://n8n.io/blog/learn-to-build-powerful-api-endpoints-using-webhooks/",
                  "icon": "🧰",
                  "label": "Learn to Build Powerful API Endpoints Using Webhooks"
                },
                {
                  "url": "https://n8n.io/blog/how-a-membership-development-manager-automates-his-work-and-investments/",
                  "icon": "📈",
                  "label": "How a Membership Development Manager automates his work and investments"
                },
                {
                  "url": "https://n8n.io/blog/a-low-code-bitcoin-ticker-built-with-questdb-and-n8n-io/",
                  "icon": "📈",
                  "label": "A low-code bitcoin ticker built with QuestDB and n8n.io"
                },
                {
                  "url": "https://n8n.io/blog/how-to-set-up-a-ci-cd-pipeline-with-no-code/",
                  "icon": "🎡",
                  "label": "How to set up a no-code CI/CD pipeline with GitHub and TravisCI"
                },
                {
                  "url": "https://n8n.io/blog/benefits-of-automation-and-n8n-an-interview-with-hubspots-hugh-durkin/",
                  "icon": "🎖",
                  "label": "Benefits of automation and n8n: An interview with HubSpot's Hugh Durkin"
                },
                {
                  "url": "https://n8n.io/blog/how-goomer-automated-their-operations-with-over-200-n8n-workflows/",
                  "icon": "🛵",
                  "label": "How Goomer automated their operations with over 200 n8n workflows"
                },
                {
                  "url": "https://n8n.io/blog/aws-workflow-automation/",
                  "label": "7 no-code workflow automations for Amazon Web Services"
                }
              ],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.set/"
                }
              ]
            },
            "categories": [
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Data Transformation"
              ]
            }
          }
        },
        "group": "[\"input\"]",
        "defaults": {
          "name": "Edit Fields"
        },
        "iconData": {
          "icon": "pen",
          "type": "icon"
        },
        "displayName": "Edit Fields (Set)",
        "typeVersion": 3,
        "nodeCategories": [
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 565,
        "icon": "fa:sticky-note",
        "name": "n8n-nodes-base.stickyNote",
        "codex": {
          "data": {
            "alias": [
              "Comments",
              "Notes",
              "Sticky"
            ],
            "categories": [
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Helpers"
              ]
            }
          }
        },
        "group": "[\"input\"]",
        "defaults": {
          "name": "Sticky Note",
          "color": "#FFD233"
        },
        "iconData": {
          "icon": "sticky-note",
          "type": "icon"
        },
        "displayName": "Sticky Note",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 834,
        "icon": "file:code.svg",
        "name": "n8n-nodes-base.code",
        "codex": {
          "data": {
            "alias": [
              "cpde",
              "Javascript",
              "JS",
              "Python",
              "Script",
              "Custom Code",
              "Function"
            ],
            "details": "The Code node allows you to execute JavaScript in your workflow.",
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/"
                }
              ]
            },
            "categories": [
              "Development",
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Helpers",
                "Data Transformation"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "Code"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDUxMiA1MTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF8xMTcxXzQ0MSkiPgo8cGF0aCBkPSJNMTcwLjI4MyA0OEgxOTYuNUMyMDMuMTI3IDQ4IDIwOC41IDQyLjYyNzQgMjA4LjUgMzZWMTJDMjA4LjUgNS4zNzI1OCAyMDMuMTI3IDAgMTk2LjUgMEgxNzAuMjgzQzEyNi4xIDAgOTAuMjgzIDM1LjgxNzIgOTAuMjgzIDgwVjE3NkM5MC4yODMgMjA2LjkyOCA2NS4yMTA5IDIzMiAzNC4yODMgMjMySDIzQzE2LjM3MjYgMjMyIDExIDIzNy4zNzIgMTEgMjQ0VjI2OEMxMSAyNzQuNjI3IDE2LjM3MjQgMjgwIDIyLjk5OTYgMjgwTDM0LjI4MyAyODBDNjUuMjEwOSAyODAgOTAuMjgzIDMwNS4wNzIgOTAuMjgzIDMzNlY0NDBDOTAuMjgzIDQ3OS43NjQgMTIyLjUxOCA1MTIgMTYyLjI4MyA1MTJIMTk2LjVDMjAzLjEyNyA1MTIgMjA4LjUgNTA2LjYyNyAyMDguNSA1MDBWNDc2QzIwOC41IDQ2OS4zNzMgMjAzLjEyNyA0NjQgMTk2LjUgNDY0SDE2Mi4yODNDMTQ5LjAyOCA0NjQgMTM4LjI4MyA0NTMuMjU1IDEzOC4yODMgNDQwVjMzNkMxMzguMjgzIDMwOS4wMjIgMTI4LjAxMSAyODQuNDQzIDExMS4xNjQgMjY1Ljk2MUMxMDYuMTA5IDI2MC40MTYgMTA2LjEwOSAyNTEuNTg0IDExMS4xNjQgMjQ2LjAzOUMxMjguMDExIDIyNy41NTcgMTM4LjI4MyAyMDIuOTc4IDEzOC4yODMgMTc2VjgwQzEzOC4yODMgNjIuMzI2OSAxNTIuNjEgNDggMTcwLjI4MyA0OFoiIGZpbGw9IiNGRjk5MjIiLz4KPHBhdGggZD0iTTMwNSAzNkMzMDUgNDIuNjI3NCAzMTAuMzczIDQ4IDMxNyA0OEgzNDIuOTc5QzM2MC42NTIgNDggMzc0Ljk3OCA2Mi4zMjY5IDM3NC45NzggODBWMTc2QzM3NC45NzggMjAyLjk3OCAzODUuMjUxIDIyNy41NTcgNDAyLjA5OCAyNDYuMDM5QzQwNy4xNTMgMjUxLjU4NCA0MDcuMTUzIDI2MC40MTYgNDAyLjA5OCAyNjUuOTYxQzM4NS4yNTEgMjg0LjQ0MyAzNzQuOTc4IDMwOS4wMjIgMzc0Ljk3OCAzMzZWNDMyQzM3NC45NzggNDQ5LjY3MyAzNjAuNjUyIDQ2NCAzNDIuOTc5IDQ2NEgzMTdDMzEwLjM3MyA0NjQgMzA1IDQ2OS4zNzMgMzA1IDQ3NlY1MDBDMzA1IDUwNi42MjcgMzEwLjM3MyA1MTIgMzE3IDUxMkgzNDIuOTc5QzM4Ny4xNjEgNTEyIDQyMi45NzggNDc2LjE4MyA0MjIuOTc4IDQzMlYzMzZDNDIyLjk3OCAzMDUuMDcyIDQ0OC4wNTEgMjgwIDQ3OC45NzkgMjgwSDQ5MEM0OTYuNjI3IDI4MCA1MDIgMjc0LjYyOCA1MDIgMjY4VjI0NEM1MDIgMjM3LjM3MyA0OTYuNjI4IDIzMiA0OTAgMjMyTDQ3OC45NzkgMjMyQzQ0OC4wNTEgMjMyIDQyMi45NzggMjA2LjkyOCA0MjIuOTc4IDE3NlY4MEM0MjIuOTc4IDM1LjgxNzIgMzg3LjE2MSAwIDM0Mi45NzkgMEgzMTdDMzEwLjM3MyAwIDMwNSA1LjM3MjU4IDMwNSAxMlYzNloiIGZpbGw9IiNGRjk5MjIiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xMTcxXzQ0MSI+CjxyZWN0IHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo="
        },
        "displayName": "Code",
        "typeVersion": 2,
        "nodeCategories": [
          {
            "id": 5,
            "name": "Development"
          },
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 839,
        "icon": "fa:clock",
        "name": "n8n-nodes-base.scheduleTrigger",
        "codex": {
          "data": {
            "alias": [
              "Time",
              "Scheduler",
              "Polling",
              "Cron",
              "Interval"
            ],
            "resources": {
              "generic": [],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.scheduletrigger/"
                }
              ]
            },
            "categories": [
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0"
          }
        },
        "group": "[\"trigger\",\"schedule\"]",
        "defaults": {
          "name": "Schedule Trigger",
          "color": "#31C49F"
        },
        "iconData": {
          "icon": "clock",
          "type": "icon"
        },
        "displayName": "Schedule Trigger",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 1119,
        "icon": "fa:robot",
        "name": "@n8n/n8n-nodes-langchain.agent",
        "codex": {
          "data": {
            "alias": [
              "LangChain",
              "Chat",
              "Conversational",
              "Plan and Execute",
              "ReAct",
              "Tools"
            ],
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/"
                }
              ]
            },
            "categories": [
              "AI",
              "Langchain"
            ],
            "subcategories": {
              "AI": [
                "Agents",
                "Root Nodes"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "AI Agent",
          "color": "#404040"
        },
        "iconData": {
          "icon": "robot",
          "type": "icon"
        },
        "displayName": "AI Agent",
        "typeVersion": 3,
        "nodeCategories": [
          {
            "id": 25,
            "name": "AI"
          },
          {
            "id": 26,
            "name": "Langchain"
          }
        ]
      },
      {
        "id": 1153,
        "icon": "file:openAiLight.svg",
        "name": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
        "codex": {
          "data": {
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.lmchatopenai/"
                }
              ]
            },
            "categories": [
              "AI",
              "Langchain"
            ],
            "subcategories": {
              "AI": [
                "Language Models",
                "Root Nodes"
              ],
              "Language Models": [
                "Chat Models (Recommended)"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "OpenAI Chat Model"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTM2Ljg2NzEgMTYuMzcxOEMzNy43NzQ2IDEzLjY0OCAzNy40NjIxIDEwLjY2NDIgMzYuMDEwOCA4LjE4NjYxQzMzLjgyODIgNC4zODY1MyAyOS40NDA3IDIuNDMxNDkgMjUuMTU1NiAzLjM1MTUxQzIzLjI0OTMgMS4yMDM5NiAyMC41MTA1IC0wLjAxNzMxNDggMTcuNjM5MiAwLjAwMDE4NTUzM0MxMy4yNTkxIC0wLjAwOTgxNDY4IDkuMzcyNzMgMi44MTAyNSA4LjAyNTIgNi45Nzc4M0M1LjIxMTM5IDcuNTU0MSAyLjc4MjU4IDkuMzE1MzggMS4zNjEzIDExLjgxMTdDLTAuODM3NDkzIDE1LjYwMTggLTAuMzM2MjMyIDIwLjM3OTQgMi42MDEzMyAyMy42Mjk0QzEuNjkzODEgMjYuMzUzMiAyLjAwNjMyIDI5LjMzNzEgMy40NTc2IDMxLjgxNDZDNS42NDAxNSAzNS42MTQ3IDEwLjAyNzcgMzcuNTY5NyAxNC4zMTI4IDM2LjY0OTdDMTYuMjE3OSAzOC43OTczIDE4Ljk1NzkgNDAuMDE4NSAyMS44MjkyIDM5Ljk5OThDMjYuMjExOCA0MC4wMTEgMzAuMDk5NCAzNy4xODg1IDMxLjQ0NjkgMzMuMDE3MUMzNC4yNjA4IDMyLjQ0MDkgMzYuNjg5NiAzMC42Nzk2IDM4LjExMDggMjguMTgzM0M0MC4zMDcxIDI0LjM5MzIgMzkuODA0NiAxOS42MTk0IDM2Ljg2ODMgMTYuMzY5M0wzNi44NjcxIDE2LjM3MThaTTIxLjgzMTcgMzcuMzg2QzIwLjA3OCAzNy4zODg1IDE4LjM3OTIgMzYuNzc0NyAxNy4wMzI5IDM1LjY1MDlDMTcuMDk0MSAzNS42MTg0IDE3LjIwMDQgMzUuNTU5NyAxNy4yNjkxIDM1LjUxNzJMMjUuMjM0MyAzMC45MTcxQzI1LjY0MTggMzAuNjg1OCAyNS44OTE4IDMwLjI1MjEgMjUuODg5MyAyOS43ODMzVjE4LjU1NDNMMjkuMjU1NyAyMC40OTgxQzI5LjI5MTkgMjAuNTE1NiAyOS4zMTU3IDIwLjU1MDYgMjkuMzIwNyAyMC41OTA2VjI5Ljg4OTZDMjkuMzE1NyAzNC4wMjQ3IDI1Ljk2NjggMzcuMzc3MiAyMS44MzE3IDM3LjM4NlpNNS43MjY0IDMwLjUwNzFDNC44NDc2MyAyOC45ODk2IDQuNTMxMzcgMjcuMjEwOCA0LjgzMjYzIDI1LjQ4NDVDNC44OTEzOCAyNS41MTk1IDQuOTk1MTMgMjUuNTgzMiA1LjA2ODg4IDI1LjYyNTdMMTMuMDM0MSAzMC4yMjU4QzEzLjQzNzggMzAuNDYyMSAxMy45Mzc4IDMwLjQ2MjEgMTQuMzQyOCAzMC4yMjU4TDI0LjA2NjggMjQuNjEwN1YyOC40OTgzQzI0LjA2OTMgMjguNTM4MyAyNC4wNTA1IDI4LjU3NyAyNC4wMTkzIDI4LjYwMkwxNS45Njc5IDMzLjI1MDlDMTIuMzgxNSAzNS4zMTU5IDcuODAxNDQgMzQuMDg4NCA1LjcyNzY1IDMwLjUwNzFINS43MjY0Wk0zLjYzMDEgMTMuMTIwNUM0LjUwNTEyIDExLjYwMDQgNS44ODY0IDEwLjQzNzkgNy41MzE0NCA5LjgzNDE1QzcuNTMxNDQgOS45MDI5IDcuNTI3NjkgMTAuMDI0MiA3LjUyNzY5IDEwLjEwOTJWMTkuMzEwNkM3LjUyNTE5IDE5Ljc3ODEgNy43NzUxOSAyMC4yMTE5IDguMTgxNDUgMjAuNDQzMUwxNy45MDU0IDI2LjA1N0wxNC41MzkxIDI4LjAwMDhDMTQuNTA1MyAyOC4wMjMzIDE0LjQ2MjggMjguMDI3IDE0LjQyNTMgMjguMDEwOEw2LjM3MjY2IDIzLjM1ODJDMi43OTM4MyAyMS4yODU2IDEuNTY2MzEgMTYuNzA2OCAzLjYyODg1IDEzLjEyMTdMMy42MzAxIDEzLjEyMDVaTTMxLjI4ODIgMTkuNTU2OUwyMS41NjQyIDEzLjk0MTdMMjQuOTMwNiAxMS45OTkyQzI0Ljk2NDMgMTEuOTc2NyAyNS4wMDY4IDExLjk3MjkgMjUuMDQ0MyAxMS45ODkyTDMzLjA5NyAxNi42MzhDMzYuNjgyMSAxOC43MDkzIDM3LjkxMDggMjMuMjk1NyAzNS44Mzk1IDI2Ljg4MDhDMzQuOTYzMyAyOC4zOTgzIDMzLjU4MzIgMjkuNTYwOCAzMS45Mzk1IDMwLjE2NThWMjAuNjg5NEMzMS45NDMyIDIwLjIyMTkgMzEuNjk0NSAxOS43ODk0IDMxLjI4OTQgMTkuNTU2OUgzMS4yODgyWk0zNC42MzgzIDE0LjUxNDJDMzQuNTc5NSAxNC40NzggMzQuNDc1OCAxNC40MTU1IDM0LjQwMiAxNC4zNzNMMjYuNDM2OCA5Ljc3Mjg5QzI2LjAzMzEgOS41MzY2NCAyNS41MzMxIDkuNTM2NjQgMjUuMTI4MSA5Ljc3Mjg5TDE1LjQwNDEgMTUuMzg4VjExLjUwMDRDMTUuNDAxNiAxMS40NjA0IDE1LjQyMDQgMTEuNDIxNyAxNS40NTE2IDExLjM5NjdMMjMuNTAzIDYuNzUxNThDMjcuMDg5NCA0LjY4Mjc5IDMxLjY3NDUgNS45MTQwNiAzMy43NDIgOS41MDE2NEMzNC42MTU4IDExLjAxNjcgMzQuOTMyIDEyLjc5MDUgMzQuNjM1OCAxNC41MTQySDM0LjYzODNaTTEzLjU3NDEgMjEuNDQzMUwxMC4yMDY1IDE5LjQ5OTRDMTAuMTcwMiAxOS40ODE5IDEwLjE0NjUgMTkuNDQ2OCAxMC4xNDE1IDE5LjQwNjhWMTAuMTA3OUMxMC4xNDQgNS45Njc4MSAxMy41MDI4IDIuNjEyNzQgMTcuNjQyOSAyLjYxNTI0QzE5LjM5NDIgMi42MTUyNCAyMS4wODkyIDMuMjMwMjUgMjIuNDM1NSA0LjM1MDI4QzIyLjM3NDMgNC4zODI3OCAyMi4yNjkzIDQuNDQxNTMgMjIuMTk5MiA0LjQ4NDAzTDE0LjIzNDEgOS4wODQxM0MxMy44MjY2IDkuMzE1MzggMTMuNTc2NiA5Ljc0Nzg5IDEzLjU3OTEgMTAuMjE2N0wxMy41NzQxIDIxLjQ0MDZWMjEuNDQzMVpNMTUuNDAyOSAxNy41MDA2TDE5LjczNDIgMTQuOTk5M0wyNC4wNjU1IDE3LjQ5OTNWMjIuNTAwN0wxOS43MzQyIDI1LjAwMDdMMTUuNDAyOSAyMi41MDA3VjE3LjUwMDZaIiBmaWxsPSIjN0Q3RDg3Ii8+Cjwvc3ZnPgo="
        },
        "displayName": "OpenAI Chat Model",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 25,
            "name": "AI"
          },
          {
            "id": 26,
            "name": "Langchain"
          }
        ]
      },
      {
        "id": 1179,
        "icon": "fa:code",
        "name": "@n8n/n8n-nodes-langchain.outputParserStructured",
        "codex": {
          "data": {
            "alias": [
              "json",
              "zod"
            ],
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.outputparserstructured/"
                }
              ]
            },
            "categories": [
              "AI",
              "Langchain"
            ],
            "subcategories": {
              "AI": [
                "Output Parsers"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "Structured Output Parser"
        },
        "iconData": {
          "icon": "code",
          "type": "icon"
        },
        "displayName": "Structured Output Parser",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 25,
            "name": "AI"
          },
          {
            "id": 26,
            "name": "Langchain"
          }
        ]
      },
      {
        "id": 1195,
        "icon": "fa:calculator",
        "name": "@n8n/n8n-nodes-langchain.toolCalculator",
        "codex": {
          "data": {
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolcalculator/"
                }
              ]
            },
            "categories": [
              "AI",
              "Langchain"
            ],
            "subcategories": {
              "AI": [
                "Tools"
              ],
              "Tools": [
                "Other Tools"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "Calculator"
        },
        "iconData": {
          "icon": "calculator",
          "type": "icon"
        },
        "displayName": "Calculator",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 25,
            "name": "AI"
          },
          {
            "id": 26,
            "name": "Langchain"
          }
        ]
      },
      {
        "id": 1197,
        "icon": "fa:code",
        "name": "@n8n/n8n-nodes-langchain.toolCode",
        "codex": {
          "data": {
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolcode/"
                }
              ]
            },
            "categories": [
              "AI",
              "Langchain"
            ],
            "subcategories": {
              "AI": [
                "Tools"
              ],
              "Tools": [
                "Recommended Tools"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "Code Tool"
        },
        "iconData": {
          "icon": "code",
          "type": "icon"
        },
        "displayName": "Code Tool",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 25,
            "name": "AI"
          },
          {
            "id": 26,
            "name": "Langchain"
          }
        ]
      },
      {
        "id": 1236,
        "icon": "file:aggregate.svg",
        "name": "n8n-nodes-base.aggregate",
        "codex": {
          "data": {
            "alias": [
              "Aggregate",
              "Combine",
              "Flatten",
              "Transform",
              "Array",
              "List",
              "Item"
            ],
            "details": "",
            "resources": {
              "generic": [],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.aggregate/"
                }
              ]
            },
            "categories": [
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Data Transformation"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "Aggregate"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1MTIiIGhlaWdodD0iNTEyIiBmaWxsPSJub25lIj48ZyBmaWxsPSIjRkY2RDVBIiBjbGlwLXBhdGg9InVybCgjYSkiPjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTMyIDE0OGMwLTYuNjI3IDUuMzczLTEyIDEyLTEyaDE0NmM2LjYyNyAwIDEyIDUuMzczIDEyIDEydjI0YzAgNi42MjctNS4zNzMgMTItMTIgMTJINDRjLTYuNjI3IDAtMTItNS4zNzMtMTItMTJ6bTAgOTZjMC02LjYyNyA1LjM3My0xMiAxMi0xMmgxNDZjNi42MjcgMCAxMiA1LjM3MyAxMiAxMnYyNGMwIDYuNjI3LTUuMzczIDEyLTEyIDEySDQ0Yy02LjYyNyAwLTEyLTUuMzczLTEyLTEyem0wIDk2YzAtNi42MjcgNS4zNzMtMTIgMTItMTJoMTQ2YzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MjRjMCA2LjYyNy01LjM3MyAxMi0xMiAxMkg0NGMtNi42MjcgMC0xMi01LjM3My0xMi0xMnoiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjxwYXRoIGQ9Ik03NCA3NmMwIDYuNjI3IDUuMzczIDEyIDEyIDEyaDExNi4yMTdjMTcuNjczIDAgMzIgMTQuMzI3IDMyIDMydjU2YzAgMjYuOTc4IDEwLjI3MiA1MS41NTcgMjcuMTE5IDcwLjAzOSA1LjA1NSA1LjU0NSA1LjA1NSAxNC4zNzcgMCAxOS45MjItMTYuODQ3IDE4LjQ4Mi0yNy4xMTkgNDMuMDYxLTI3LjExOSA3MC4wMzl2NTZjMCAxNy42NzMtMTQuMzI3IDMyLTMyIDMySDg2Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0YzAgNi42MjcgNS4zNzMgMTIgMTIgMTJoMTE2LjIxN2M0NC4xODMgMCA4MC0zNS44MTcgODAtODB2LTU2YzAtMzAuOTI4IDI1LjA3Mi01NiA1Ni01NmE1Ljc4MyA1Ljc4MyAwIDAgMCA1Ljc4My01Ljc4M3YtMzYuNDM0YTUuNzgzIDUuNzgzIDAgMCAwLTUuNzgzLTUuNzgzYy0zMC45MjggMC01Ni0yNS4wNzItNTYtNTZ2LTU2YzAtNDQuMTgzLTM1LjgxNy04MC04MC04MEg4NmMtNi42MjcgMC0xMiA1LjM3My0xMiAxMnoiLz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zNzYgMjQ0YzAtNi42MjcgNS4zNzMtMTIgMTItMTJoMTEyYzYuNjI3IDAgMTIgNS4zNzMgMTIgMTJ2MjRjMCA2LjYyNy01LjM3MyAxMi0xMiAxMkgzODhjLTYuNjI3IDAtMTItNS4zNzMtMTItMTJ6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiLz48L2c+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz48L2NsaXBQYXRoPjwvZGVmcz48L3N2Zz4="
        },
        "displayName": "Aggregate",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      }
    ],
    "categories": [
      {
        "id": 32,
        "name": "Market Research"
      },
      {
        "id": 49,
        "name": "AI Summarization"
      }
    ],
    "image": []
  }
}