{
  "workflow": {
    "id": 9788,
    "name": "Compare flight prices across multiple booking platforms with email reports",
    "views": 534,
    "recentViews": 1,
    "totalViews": 534,
    "createdAt": "2025-10-16T15:15:14.462Z",
    "description": "This workflow automates flight price comparison across multiple booking platforms (Kayak, Skyscanner, Expedia, Google Flights). It accepts natural language queries, extracts flight details using NLP, scrapes prices in parallel, identifies the best deals, and sends professional email reports with comprehensive price breakdowns and booking links.\n\n## 📦 What You'll Get\n\nA **fully functional, production-ready** n8n workflow that:\n\n✅ Compares flight prices across 4 major platforms (Kayak, Skyscanner, Expedia, Google Flights)  \n✅ Accepts natural language requests (\"Flight from NYC to London on March 25\")  \n✅ Sends beautiful email reports with best deals  \n✅ Returns real-time JSON responses for web apps  \n✅ Handles errors gracefully with helpful messages  \n✅ Includes detailed documentation with sticky notes\n\n---\n\n## 🚀 Quick Setup (3 Steps)\n\n### Step 1: Import Workflow to n8n\n\n1. **Copy the JSON** from the first artifact (workflow file)\n2. **Open n8n** → Go to **Workflows**\n3. **Click \"Import from File\"** → **Paste JSON** → Click **Import**\n4. ✅ Workflow imported successfully!\n\n### Step 2: Setup Python Scraper\n\n**On your server (where n8n SSH nodes will connect):**\n\n```bash\n# Navigate to your scripts directory\ncd /home/oneclick-server2/\n\n# Create the scraper file\nnano flight_scraper.py\n\n# Copy the entire Python script from the second artifact\n# Save with Ctrl+X, then Y, then Enter\n\n# Make it executable\nchmod +x flight_scraper.py\n\n# Install required packages\npip3 install selenium\n\n# Install Chrome and ChromeDriver\nsudo apt update\nsudo apt install -y chromium-browser chromium-chromedriver\n\n# Test the scraper\npython3 flight_scraper.py JFK LHR 2025-03-25 2025-03-30 round-trip 1 economy kayak\n```\n\n**Expected Output:**\n```\nDelta|$450|7h 30m|0|10:00 AM|6:30 PM|https://kayak.com/...\nBritish Airways|$485|7h 45m|0|11:30 AM|8:15 PM|https://kayak.com/...\n...\n```\n\n### Step 3: Configure n8n Credentials\n\n**A. Setup SMTP (for sending emails):**\n\n1. In n8n: **Credentials** → **Add Credential** → **SMTP**\n2. Fill in details:\n   ```\n   Host: smtp.gmail.com\n   Port: 587\n   User: your-email@gmail.com\n   Password: [Your App Password]\n   ```\n\n**For Gmail Users:**\n- Enable 2FA: https://myaccount.google.com/security\n- Create App Password: https://myaccount.google.com/apppasswords\n- Use the 16-character password in n8n\n\n**B. Setup SSH (already configured if you used existing credentials):**\n\n1. In workflow, SSH nodes use: `ilPh8oO4GfSlc0Qy`\n2. Verify credential exists and points to correct server\n3. Update path if needed: `/home/oneclick-server2/`\n\n**C. Activate Workflow:**\n\n1. Click the workflow **toggle** → **Active** ✅\n2. Webhook is now live!\n\n---\n\n## 🎯 How to Use\n\n### Method 1: Direct Webhook Call\n\n```bash\ncurl -X POST https://your-n8n-domain.com/webhook/flight-price-compare \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"message\": \"Flight from Mumbai to Dubai on 15th March, round-trip returning 20th March\",\n    \"email\": \"user@example.com\",\n    \"name\": \"John Doe\"\n  }'\n```\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"message\": \"Flight comparison sent to user@example.com\",\n  \"route\": \"BOM → DXB\",\n  \"bestPrice\": 450,\n  \"airline\": \"Emirates\",\n  \"totalResults\": 18\n}\n```\n\n### Method 2: Natural Language Queries\n\nThe workflow understands various formats:\n\n**✅ All these work:**\n- \"Flight from New York to London on 25th March, one-way\"\n- \"NYC to LHR March 25 round-trip return March 30\"\n- \"I need a flight from Mumbai to Dubai departing 15th March\"\n- \"JFK LHR 2025-03-25 2025-03-30 round-trip\"\n\n**Supported cities (auto-converts to airport codes):**\n- New York → JFK\n- London → LHR\n- Mumbai → BOM\n- Dubai → DXB\n- Singapore → SIN\n- And 20+ more cities\n\n### Method 3: Structured JSON\n\n```json\n{\n  \"from\": \"JFK\",\n  \"to\": \"LHR\",\n  \"departure_date\": \"2025-03-25\",\n  \"return_date\": \"2025-03-30\",\n  \"trip_type\": \"round-trip\",\n  \"passengers\": 1,\n  \"class\": \"economy\",\n  \"email\": \"user@example.com\",\n  \"name\": \"John\"\n}\n```\n\n---\n\n## 📧 Email Report Example\n\nUsers receive an email like this:\n\n```\nFLIGHT PRICE COMPARISON\n==================================================\n\nRoute: JFK → LHR\nDeparture: 25 Mar 2025\nReturn: 30 Mar 2025\nTrip Type: round-trip\nPassengers: 1\n\n🏆 BEST DEAL\n--------------------------------------------------\nBritish Airways\nPrice: $450\nDuration: 7h 30m\nStops: Non-stop\nPlatform: Kayak\n\n💰 Save $85 vs highest price!\n\n📊 ALL RESULTS (Top 10)\n--------------------------------------------------\n1. British Airways - $450 (Non-stop) - Kayak\n2. Delta - $475 (Non-stop) - Google Flights\n3. American Airlines - $485 (Non-stop) - Expedia\n4. Virgin Atlantic - $495 (Non-stop) - Skyscanner\n5. United - $520 (1 stop) - Kayak\n...\n\nAverage Price: $495\nTotal Results: 23\n\nPrices subject to availability.\nHappy travels! ✈️\n```\n\n---\n\n## 🔧 Customization Options\n\n### Change Scraping Platforms\n\n**Add more platforms:**\n\n1. Duplicate an SSH scraping node\n2. Change platform parameter: `kayak` → `new-platform`\n3. Add scraping logic in `flight_scraper.py`\n4. Connect to \"Aggregate & Analyze Prices\" node\n\n**Remove platforms:**\n\n1. Delete unwanted SSH node\n2. Workflow continues with remaining platforms\n\n### Modify Email Format\n\nEdit the \"Format Email Report\" node:\n\n```javascript\n// Change to HTML format\nconst html = `\n&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;body&gt;\n  <h1>Flight Deals</h1>\n  <p>Best price: ${bestDeal.currency}${bestDeal.price}</p>\n&lt;/body&gt;\n&lt;/html&gt;\n`;\n\nreturn [{\n  json: {\n    subject: \"...\",\n    html: html,  // Instead of text\n    ...data\n  }\n}];\n```\n\nThen update \"Send Email Report\" node:\n- Change `emailFormat` to `html`\n- Use `{{$json.html}}` instead of `{{$json.text}}`\n\n### Add More Cities/Airports\n\nEdit \"Parse & Validate Flight Request\" node:\n\n```javascript\nconst airportCodes = {\n  ...existing codes...,\n  'berlin': 'BER',\n  'rome': 'FCO',\n  'barcelona': 'BCN',\n  // Add your cities here\n};\n```\n\n### Change Timeout Settings\n\nIn each SSH node, add:\n\n```javascript\n\"timeout\": 30000  // 30 seconds\n```\n\n---\n\n## 🐛 Troubleshooting\n\n### Issue: \"No flights found\"\n\n**Possible causes:**\n1. Scraper script not working\n2. Website structure changed\n3. Dates in past\n4. Invalid airport codes\n\n**Solutions:**\n```bash\n# Test scraper manually\ncd /home/oneclick-server2/\npython3 flight_scraper.py JFK LHR 2025-03-25 \"\" one-way 1 economy kayak\n\n# Check if output shows flights\n# If no output, check Chrome/ChromeDriver installation\n```\n\n### Issue: \"Connection refused\" (SSH)\n\n**Solutions:**\n1. Verify SSH credentials in n8n\n2. Check server is accessible: `ssh user@your-server`\n3. Verify path exists: `/home/oneclick-server2/`\n4. Check Python installed: `which python3`\n\n### Issue: \"Email not sending\"\n\n**Solutions:**\n1. Verify SMTP credentials\n2. Check email in spam folder\n3. For Gmail: Confirm App Password is used (not regular password)\n4. Test SMTP connection:\n   ```bash\n   telnet smtp.gmail.com 587\n   ```\n\n### Issue: \"Webhook not responding\"\n\n**Solutions:**\n1. Ensure workflow is **Active** (toggle on)\n2. Check webhook path: `/webhook/flight-price-compare`\n3. Test with curl command (see \"How to Use\" section)\n4. Check n8n logs: Settings → Log Streaming\n\n### Issue: \"Scraper timing out\"\n\n**Solutions:**\n```python\n# In flight_scraper.py, increase wait times\ntime.sleep(10)  # Instead of time.sleep(5)\n\n# Or increase WebDriverWait timeout\nWebDriverWait(driver, 30)  # Instead of 20\n```\n\n---\n\n## 📊 Understanding the Workflow\n\n### Node-by-Node Explanation\n\n**1. Webhook - Receive Flight Request**\n- Entry point for all requests\n- Accepts POST requests\n- Path: `/webhook/flight-price-compare`\n\n**2. Parse & Validate Flight Request**\n- Extracts flight details from natural language\n- Converts city names to airport codes\n- Validates required fields\n- Returns helpful errors if data missing\n\n**3. Check If Request Valid**\n- Routes to scraping if valid\n- Routes to error response if invalid\n\n**4-7. Scrape [Platform] (4 nodes)**\n- Run in parallel for speed\n- Each calls Python script with platform parameter\n- Continue on failure (don't break workflow)\n- Return pipe-delimited flight data\n\n**8. Aggregate & Analyze Prices**\n- Collects all scraper results\n- Parses flight data\n- Finds best overall deal\n- Finds best non-stop flight\n- Calculates statistics\n- Sorts by price\n\n**9. Format Email Report**\n- Creates readable text report\n- Includes route details\n- Highlights best deal\n- Lists top 10 results\n- Shows statistics\n\n**10. Send Email Report**\n- Sends formatted email to user\n- Uses SMTP credentials\n\n**11. Webhook Response (Success)**\n- Returns JSON response immediately\n- Includes best price summary\n- Confirms email sent\n\n**12. Webhook Response (Error)**\n- Returns helpful error message\n- Guides user on what's missing\n\n---\n\n## 🎨 Workflow Features\n\n### ✅ Included Features\n\n- **Natural Language Processing**: Understands flexible input formats\n- **Multi-Platform Comparison**: 4 major booking sites\n- **Parallel Scraping**: All platforms scraped simultaneously\n- **Error Handling**: Graceful failures, helpful messages\n- **Email Reports**: Professional format with all details\n- **Real-Time Responses**: Instant webhook feedback\n- **Sticky Notes**: Detailed documentation in workflow\n- **Airport Code Mapping**: Auto-converts 20+ cities\n\n### 🚧 Not Included (Easy to Add)\n\n- **Price Alerts**: Monitor price drops (add Google Sheets)\n- **Analytics Dashboard**: Track searches (add Google Sheets)\n- **SMS Notifications**: Send via Twilio\n- **Slack Integration**: Post to channels\n- **Database Logging**: Store searches in PostgreSQL\n- **Multi-Currency**: Show prices in the user's currency\n\n---\n\n## 💡 Pro Tips\n\n### Tip 1: Speed Up Scraping\n\nUse faster scraping service (like ScraperAPI):\n\n```javascript\n// Replace SSH nodes with HTTP Request nodes\n{\n  \"url\": \"http://api.scraperapi.com\",\n  \"qs\": {\n    \"api_key\": \"YOUR_KEY\",\n    \"url\": \"https://kayak.com/flights/...\"\n  }\n}\n```\n\n### Tip 2: Cache Results\n\nAdd caching to avoid duplicate scraping:\n\n```javascript\n// In Parse node, check cache first\nconst cacheKey = `${origin}-${dest}-${departureDate}`;\nconst cached = await $cache.get(cacheKey);\n\nif (cached && Date.now() - cached.time &lt; 3600000) {\n  return cached.data; // Use 1-hour cache\n}\n```\n\n### Tip 3: Add More Platforms\n\nEasy to add Momondo, CheapOair, etc.:\n\n1. Add function in `flight_scraper.py`\n2. Add SSH node in workflow\n3. Connect to aggregator\n\n### Tip 4: Improve Date Parsing\n\nHandle more formats:\n\n```javascript\n// Add to Parse node\nconst formats = [\n  'DD/MM/YYYY',\n  'MM-DD-YYYY',\n  'YYYY.MM.DD',\n  // Add your formats\n];\n```\n\n\n",
    "workflow": {
      "id": "IuvYgHrzvtEck50d",
      "meta": {
        "instanceId": "dd69efaf8212c74ad206700d104739d3329588a6f3f8381a46a481f34c9cc281",
        "templateCredsSetupCompleted": true
      },
      "name": "Automate flight Price Comparison",
      "tags": [],
      "nodes": [
        {
          "id": "d4a5235a-57c0-42f6-9841-8f45d04dfe1c",
          "name": "Webhook - Receive Flight Request",
          "type": "n8n-nodes-base.webhook",
          "position": [
            -1088,
            432
          ],
          "webhookId": "flight-price-webhook",
          "parameters": {
            "path": "flight-price-compare",
            "options": {
              "allowedOrigins": "*"
            },
            "httpMethod": "POST",
            "responseMode": "responseNode"
          },
          "typeVersion": 2.1
        },
        {
          "id": "81bb3dac-0cd3-4826-8436-573bc393f2c4",
          "name": "Parse & Validate Flight Request",
          "type": "n8n-nodes-base.code",
          "position": [
            -864,
            432
          ],
          "parameters": {
            "jsCode": "// Enhanced Flight Request Parser with NLP\nconst body = $input.first().json.body || {};\nconst query = body.message || body.query || '';\n\n// Extract user details\nconst userEmail = body.email || body.user_email || '';\nconst userName = body.name || body.user_name || 'Traveler';\nconst notifyPriceDrop = body.notify_price_drop || false;\n\n// Greeting handler\nconst greetings = ['hi', 'hello', 'hey', 'start', 'help'];\nif (greetings.some(g => query.toLowerCase().includes(g)) && query.split(' ').length < 5) {\n  return [{\n    json: {\n      status: 'greeting',\n      response: `Hi ${userName}! ✈️ I'm your Flight Price Comparison Assistant.\\n\\nTell me:\\n✅ From city/airport\\n✅ To city/airport\\n✅ Departure date\\n✅ Trip type (one-way/round-trip)\\n\\nExample: \"Flight from New York to London on 25th March, one-way\"`,\n      userEmail,\n      userName\n    }\n  }];\n}\n\n// Airport/City codes mapping\nconst airportCodes = {\n  'new york': 'JFK', 'nyc': 'JFK', 'london': 'LHR', 'paris': 'CDG',\n  'dubai': 'DXB', 'singapore': 'SIN', 'tokyo': 'NRT', 'mumbai': 'BOM',\n  'delhi': 'DEL', 'bangalore': 'BLR', 'los angeles': 'LAX', 'chicago': 'ORD',\n  'san francisco': 'SFO', 'boston': 'BOS', 'miami': 'MIA', 'sydney': 'SYD',\n  'melbourne': 'MEL', 'hong kong': 'HKG', 'bangkok': 'BKK', 'amsterdam': 'AMS',\n  'frankfurt': 'FRA', 'toronto': 'YYZ', 'vancouver': 'YVR', 'seattle': 'SEA'\n};\n\nfunction getAirportCode(text) {\n  const lower = text.toLowerCase().trim();\n  if (/^[A-Z]{3}$/i.test(text)) return text.toUpperCase();\n  return airportCodes[lower] || text.toUpperCase();\n}\n\n// Parse dates\nfunction parseDate(text) {\n  const monthMap = {\n    jan: 0, january: 0, feb: 1, february: 1, mar: 2, march: 2,\n    apr: 3, april: 3, may: 4, jun: 5, june: 5,\n    jul: 6, july: 6, aug: 7, august: 7, sep: 8, september: 8,\n    oct: 9, october: 9, nov: 10, november: 10, dec: 11, december: 11\n  };\n\n  const dateRegex = /(\\d{1,2})(st|nd|rd|th)?\\s+(jan|january|feb|february|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|september|oct|october|nov|november|dec|december)|\\d{4}-\\d{2}-\\d{2}/gi;\n  const matches = [...text.matchAll(dateRegex)];\n  const dates = [];\n  const currentYear = new Date().getFullYear();\n  \n  matches.forEach(match => {\n    if (match[0].includes('-')) {\n      dates.push(new Date(match[0]));\n    } else {\n      const day = parseInt(match[1]);\n      const monthStr = match[3].toLowerCase();\n      const month = monthMap[monthStr];\n      if (day && month !== undefined) {\n        dates.push(new Date(currentYear, month, day));\n      }\n    }\n  });\n  \n  return dates.sort((a, b) => a - b);\n}\n\n// Extract origin and destination\nlet origin = '';\nlet destination = '';\n\nconst routePattern = /(?:from|leaving)?\\s*([a-z\\s]{3,25})\\s+to\\s+([a-z\\s]{3,25})/i;\nconst routeMatch = query.match(routePattern);\n\nif (routeMatch) {\n  origin = routeMatch[1].trim();\n  destination = routeMatch[2].trim();\n} else {\n  origin = body.from || body.origin || body.departure_airport || '';\n  destination = body.to || body.destination || body.arrival_airport || '';\n}\n\nconst originCode = getAirportCode(origin);\nconst destinationCode = getAirportCode(destination);\n\nconst dates = parseDate(query + ' ' + (body.departure_date || ''));\nlet departureDate = dates[0] || null;\nlet returnDate = dates[1] || null;\n\nlet tripType = 'one-way';\nif (query.match(/round[\\s-]?trip|return/i) || returnDate) {\n  tripType = 'round-trip';\n}\nif (body.trip_type) {\n  tripType = body.trip_type.toLowerCase();\n}\n\nconst passengers = body.passengers || 1;\nconst cabinClass = body.class || body.cabin_class || 'economy';\n\nconst errors = [];\nif (!originCode || originCode.length < 3) errors.push('departure city/airport');\nif (!destinationCode || destinationCode.length < 3) errors.push('arrival city/airport');\nif (!departureDate) errors.push('departure date');\nif (tripType === 'round-trip' && !returnDate) errors.push('return date');\n\nif (errors.length > 0) {\n  return [{\n    json: {\n      status: 'missing_info',\n      response: `I need more information: ${errors.join(', ')}.\\n\\nExample: \"Flight from Mumbai to Dubai on 15th March, round-trip returning 20th March\"`,\n      userEmail,\n      userName\n    }\n  }];\n}\n\nconst formatDate = (date) => {\n  if (!date) return null;\n  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n  return `${date.getDate()} ${months[date.getMonth()]} ${date.getFullYear()}`;\n};\n\nconst formatDateISO = (date) => {\n  if (!date) return null;\n  return date.toISOString().split('T')[0];\n};\n\nreturn [{\n  json: {\n    status: 'ready',\n    origin: originCode,\n    destination: destinationCode,\n    originCity: origin,\n    destinationCity: destination,\n    departureDate: formatDate(departureDate),\n    returnDate: returnDate ? formatDate(returnDate) : null,\n    departureDateISO: formatDateISO(departureDate),\n    returnDateISO: formatDateISO(returnDate),\n    tripType,\n    passengers,\n    cabinClass,\n    userEmail,\n    userName,\n    notifyPriceDrop,\n    originalQuery: query\n  }\n}];"
          },
          "typeVersion": 2
        },
        {
          "id": "3f5bb362-ca6a-4c1d-a959-9f194fcc7e99",
          "name": "Check If Request Valid",
          "type": "n8n-nodes-base.if",
          "position": [
            -640,
            432
          ],
          "parameters": {
            "conditions": {
              "string": [
                {
                  "value1": "={{$json.status}}",
                  "value2": "ready"
                }
              ]
            }
          },
          "typeVersion": 1
        },
        {
          "id": "07df7d09-a38d-4540-b8f8-0e81405e086b",
          "name": "Scrape Kayak",
          "type": "n8n-nodes-base.ssh",
          "onError": "continueErrorOutput",
          "position": [
            -416,
            48
          ],
          "parameters": {
            "cwd": "/home/oneclick-server2/",
            "command": "=python3 /home/oneclick-server2/flight_scraper.py {{ $json.origin }} {{ $json.destination }} {{ $json.departureDateISO }} {{ $json.returnDateISO || '' }} {{ $json.tripType }} {{ $json.passengers }} {{ $json.cabinClass }} kayak",
            "authentication": "privateKey"
          },
          "credentials": {
            "sshPrivateKey": {
              "id": "credential-id",
              "name": "sshPrivateKey Credential"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "2acab75d-4d75-49d7-8c37-dc36740e7636",
          "name": "Scrape Skyscanner",
          "type": "n8n-nodes-base.ssh",
          "onError": "continueErrorOutput",
          "position": [
            -416,
            240
          ],
          "parameters": {
            "cwd": "/home/oneclick-server2/",
            "command": "=python3 /home/oneclick-server2/flight_scraper.py {{ $json.origin }} {{ $json.destination }} {{ $json.departureDateISO }} {{ $json.returnDateISO || '' }} {{ $json.tripType }} {{ $json.passengers }} {{ $json.cabinClass }} skyscanner",
            "authentication": "privateKey"
          },
          "credentials": {
            "sshPrivateKey": {
              "id": "credential-id",
              "name": "sshPrivateKey Credential"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "b74690a3-61b9-41df-967b-bf395fb6aade",
          "name": "Scrape Expedia",
          "type": "n8n-nodes-base.ssh",
          "onError": "continueErrorOutput",
          "position": [
            -416,
            432
          ],
          "parameters": {
            "cwd": "/home/oneclick-server2/",
            "command": "=python3 /home/oneclick-server2/flight_scraper.py {{ $json.origin }} {{ $json.destination }} {{ $json.departureDateISO }} {{ $json.returnDateISO || '' }} {{ $json.tripType }} {{ $json.passengers }} {{ $json.cabinClass }} expedia",
            "authentication": "privateKey"
          },
          "credentials": {
            "sshPrivateKey": {
              "id": "credential-id",
              "name": "sshPrivateKey Credential"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "76e69f7a-9683-47ce-bdf0-b673c504a4b2",
          "name": "Scrape Google Flights",
          "type": "n8n-nodes-base.ssh",
          "onError": "continueErrorOutput",
          "position": [
            -416,
            816
          ],
          "parameters": {
            "cwd": "/home/oneclick-server2/",
            "command": "=python3 /home/oneclick-server2/flight_scraper.py {{ $json.origin }} {{ $json.destination }} {{ $json.departureDateISO }} {{ $json.returnDateISO || '' }} {{ $json.tripType }} {{ $json.passengers }} {{ $json.cabinClass }} googleflights",
            "authentication": "privateKey"
          },
          "credentials": {
            "sshPrivateKey": {
              "id": "credential-id",
              "name": "sshPrivateKey Credential"
            }
          },
          "typeVersion": 1
        },
        {
          "id": "020178b1-0de8-4cbc-812d-bec95ad92d8f",
          "name": "Aggregate & Analyze Prices",
          "type": "n8n-nodes-base.code",
          "position": [
            -64,
            336
          ],
          "parameters": {
            "jsCode": "// Aggregate flight prices from all platforms\nconst items = $input.all();\nconst searchData = items[0].json;\n\nconst flights = [];\nconst errors = [];\n\nconst platforms = ['Kayak', 'Skyscanner', 'Expedia', 'Google Flights'];\n\nitems.slice(1).forEach((item, index) => {\n  const platform = platforms[index];\n  const output = item.json.stdout || '';\n  \n  if (item.json.stderr || !output) {\n    errors.push(`${platform}: Unable to fetch`);\n    return;\n  }\n  \n  const lines = output.trim().split('\\n');\n  \n  lines.forEach(line => {\n    if (line.includes('|')) {\n      const parts = line.split('|');\n      if (parts.length >= 6) {\n        const price = parseFloat(parts[1].replace(/[^0-9.]/g, ''));\n        \n        if (price && price > 0) {\n          flights.push({\n            platform,\n            airline: parts[0].trim(),\n            price,\n            currency: parts[1].match(/[A-Z₹$€£]/)?.[0] || '$',\n            duration: parts[2].trim(),\n            stops: parts[3].trim(),\n            departureTime: parts[4].trim(),\n            arrivalTime: parts[5].trim(),\n            bookingUrl: parts[6]?.trim() || '#',\n            cabinClass: searchData.cabinClass\n          });\n        }\n      }\n    }\n  });\n});\n\nif (flights.length === 0) {\n  return [{\n    json: {\n      status: 'no_results',\n      message: 'No flights found for your search criteria.',\n      errors,\n      ...searchData\n    }\n  }];\n}\n\nflights.sort((a, b) => a.price - b.price);\n\nconst bestDeal = flights[0];\nconst avgPrice = flights.reduce((sum, f) => sum + f.price, 0) / flights.length;\nconst maxPrice = flights[flights.length - 1].price;\nconst savings = maxPrice - bestDeal.price;\n\nconst directFlights = flights.filter(f => f.stops === '0' || f.stops.toLowerCase().includes('non'));\nconst bestDirectFlight = directFlights.length > 0 ? directFlights[0] : null;\n\nreturn [{\n  json: {\n    status: 'success',\n    ...searchData,\n    results: flights,\n    bestDeal,\n    bestDirectFlight,\n    avgPrice: Math.round(avgPrice),\n    maxPrice: Math.round(maxPrice),\n    savings: Math.round(savings),\n    totalResults: flights.length,\n    directFlightsCount: directFlights.length,\n    errors,\n    searchTimestamp: new Date().toISOString()\n  }\n}];"
          },
          "typeVersion": 2
        },
        {
          "id": "b195bb98-d48b-442d-8ceb-7809a2f65d15",
          "name": "Format Email Report",
          "type": "n8n-nodes-base.code",
          "position": [
            256,
            336
          ],
          "parameters": {
            "jsCode": "// Format email report\nconst data = $input.first().json;\n\nif (data.status === 'no_results') {\n  return [{\n    json: {\n      subject: `❌ No Flights - ${data.origin} to ${data.destination}`,\n      text: `No flights found for ${data.origin} to ${data.destination} on ${data.departureDate}`,\n      ...data\n    }\n  }];\n}\n\nconst { origin, destination, departureDate, returnDate, tripType, passengers, results, bestDeal, avgPrice, savings } = data;\n\nconst topResults = results.slice(0, 10);\nconst resultsText = topResults.map((f, i) => \n  `${i + 1}. ${f.airline} - ${f.currency}${f.price} (${f.stops === '0' ? 'Non-stop' : f.stops + ' stop(s)'}) - ${f.platform}`\n).join('\\n');\n\nconst textReport = `\nFLIGHT PRICE COMPARISON\n${'='.repeat(50)}\n\nRoute: ${origin} → ${destination}\nDeparture: ${departureDate}\n${returnDate ? `Return: ${returnDate}\\n` : ''}Trip Type: ${tripType}\nPassengers: ${passengers}\n\n🏆 BEST DEAL\n${'-'.repeat(50)}\n${bestDeal.airline}\nPrice: ${bestDeal.currency}${bestDeal.price}\nDuration: ${bestDeal.duration}\nStops: ${bestDeal.stops === '0' ? 'Non-stop' : bestDeal.stops + ' stop(s)'}\nPlatform: ${bestDeal.platform}\n${savings > 0 ? `\\n💰 Save ${bestDeal.currency}${savings} vs highest price!` : ''}\n\n📊 ALL RESULTS (Top 10)\n${'-'.repeat(50)}\n${resultsText}\n\nAverage Price: ${bestDeal.currency}${avgPrice}\nTotal Results: ${results.length}\n\nPrices subject to availability.\nHappy travels! ✈️\n`;\n\nreturn [{\n  json: {\n    subject: `✈️ ${origin} → ${destination} - Best: ${bestDeal.currency}${bestDeal.price}`,\n    text: textReport,\n    ...data\n  }\n}];"
          },
          "typeVersion": 2
        },
        {
          "id": "994f5fa6-2e45-42f7-af12-f98026250c04",
          "name": "Send Email Report",
          "type": "n8n-nodes-base.emailSend",
          "position": [
            480,
            336
          ],
          "webhookId": "7e60915f-7994-42f1-b850-dcb254a4d83c",
          "parameters": {
            "text": "={{$json.text}}",
            "options": {},
            "subject": "={{$json.subject}}",
            "toEmail": "={{$json.userEmail}}",
            "fromEmail": "user@example.com",
            "emailFormat": "text"
          },
          "credentials": {
            "smtp": {
              "id": "credential-id",
              "name": "smtp Credential"
            }
          },
          "typeVersion": 2.1
        },
        {
          "id": "8b3fbdb5-879f-4a02-8927-8632f944bd53",
          "name": "Webhook Response (Success)",
          "type": "n8n-nodes-base.respondToWebhook",
          "position": [
            784,
            336
          ],
          "parameters": {
            "options": {},
            "respondWith": "json",
            "responseBody": "={{ {\n  \"success\": true,\n  \"message\": \"Flight comparison sent to \" + $json.userEmail,\n  \"route\": $json.origin + \" → \" + $json.destination,\n  \"bestPrice\": $json.bestDeal.price,\n  \"airline\": $json.bestDeal.airline,\n  \"totalResults\": $json.totalResults\n} }}"
          },
          "typeVersion": 1.1
        },
        {
          "id": "d1d130de-f7f2-42ed-9f2b-591446329e6c",
          "name": "Webhook Response (Error)",
          "type": "n8n-nodes-base.respondToWebhook",
          "position": [
            -416,
            624
          ],
          "parameters": {
            "options": {},
            "respondWith": "json",
            "responseBody": "={{ {\n  \"success\": false,\n  \"message\": $json.response || \"Request failed\",\n  \"status\": $json.status\n} }}"
          },
          "typeVersion": 1.1
        },
        {
          "id": "35dd7ec1-cb14-4b34-9138-0d8e8af2380a",
          "name": "Sticky Note",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -1184,
            -624
          ],
          "parameters": {
            "color": 4,
            "width": 484,
            "height": 476,
            "content": "## 🎯 Workflow Purpose\n\n**Smart Flight Price Comparison**\n\nAutomatically compares flight prices across multiple booking platforms and sends detailed reports via email.\n\n### Key Features:\n✅ Natural language input\n✅ Multi-platform scraping\n✅ Best deal identification  \n✅ Email reports\n✅ Real-time responses\n\n### Platforms Compared:\n- Kayak\n- Skyscanner\n- Expedia\n- Google Flights"
          },
          "typeVersion": 1
        },
        {
          "id": "c312076a-7442-41fe-ac65-692581acde7d",
          "name": "Sticky Note1",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -1408,
            96
          ],
          "parameters": {
            "color": 5,
            "width": 452,
            "height": 516,
            "content": "## 📥 INPUT STAGE\n\n**Webhook receives:**\n- Flight search query\n- User email\n- Alert preferences\n\n**Example:**\n```json\n{\n  \"message\": \"NYC to London March 25\",\n  \"email\": \"user@test.com\"\n}\n```"
          },
          "typeVersion": 1
        },
        {
          "id": "f8aa3a61-314d-4b4e-83bb-477f50a7562a",
          "name": "Sticky Note2",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -928,
            48
          ],
          "parameters": {
            "color": 5,
            "width": 260,
            "height": 612,
            "content": "## 🧠 PARSE STAGE\n\n**Extracts:**\n- Airport codes\n- Dates (ISO format)\n- Trip type\n- Passengers\n\n**Validates:**\n- Required fields\n- Date formats\n- Airport codes"
          },
          "typeVersion": 1
        },
        {
          "id": "32ea52fc-41ef-4f5c-bf12-c0b13b9a2866",
          "name": "Sticky Note3",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -508,
            -280
          ],
          "parameters": {
            "color": 5,
            "width": 280,
            "height": 1224,
            "content": "## 🔍 SCRAPE STAGE\n\n**Parallel scraping:**\n- All platforms simultaneously\n- Continue on failures\n- 30s timeout per scraper\n\n**Output format:**\nAIRLINE|PRICE|DURATION|STOPS|TIME|TIME|URL"
          },
          "typeVersion": 1
        },
        {
          "id": "d503d1f7-06ca-4d75-96fc-b0a6d3fccb1a",
          "name": "Sticky Note4",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            -112,
            -32
          ],
          "parameters": {
            "color": 5,
            "width": 260,
            "height": 660,
            "content": "## 📊 ANALYZE STAGE\n\n**Processes:**\n- Parse all results\n- Find best deals\n- Calculate stats\n- Sort by price\n\n**Outputs:**\n- Best overall\n- Best direct\n- Avg price\n- Savings"
          },
          "typeVersion": 1
        },
        {
          "id": "b194c549-bdad-4f31-9ea3-5990caea9b84",
          "name": "Sticky Note5",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            224,
            0
          ],
          "parameters": {
            "color": 5,
            "width": 388,
            "height": 628,
            "content": "## 📧 REPORT STAGE\n\n**Email contains:**\n- Flight route & dates\n- Best deal highlight\n- Top 10 results\n- Price statistics\n- Booking links\n\n**Format:**\nPlain text (easy to read)"
          },
          "typeVersion": 1
        },
        {
          "id": "0e963069-8538-431f-977b-9832b8905af4",
          "name": "Sticky Note6",
          "type": "n8n-nodes-base.stickyNote",
          "position": [
            688,
            0
          ],
          "parameters": {
            "color": 5,
            "width": 260,
            "height": 612,
            "content": "## ✅ RESPONSE STAGE\n\n**Success:**\n- Best price found\n- Airline name\n- Total results\n- Email sent confirmation\n\n**Error:**\n- Helpful message\n- What's missing\n- Example format"
          },
          "typeVersion": 1
        }
      ],
      "active": false,
      "pinData": {},
      "settings": {
        "executionOrder": "v1"
      },
      "versionId": "8562102b-7099-407b-ae1f-69da37f8da99",
      "connections": {
        "Scrape Kayak": {
          "main": [
            [
              {
                "node": "Aggregate & Analyze Prices",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Scrape Expedia": {
          "main": [
            [
              {
                "node": "Aggregate & Analyze Prices",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Scrape Skyscanner": {
          "main": [
            [
              {
                "node": "Aggregate & Analyze Prices",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Send Email Report": {
          "main": [
            [
              {
                "node": "Webhook Response (Success)",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Format Email Report": {
          "main": [
            [
              {
                "node": "Send Email Report",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Scrape Google Flights": {
          "main": [
            [
              {
                "node": "Aggregate & Analyze Prices",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Check If Request Valid": {
          "main": [
            [
              {
                "node": "Scrape Kayak",
                "type": "main",
                "index": 0
              },
              {
                "node": "Scrape Skyscanner",
                "type": "main",
                "index": 0
              },
              {
                "node": "Scrape Expedia",
                "type": "main",
                "index": 0
              },
              {
                "node": "Scrape Google Flights",
                "type": "main",
                "index": 0
              }
            ],
            [
              {
                "node": "Webhook Response (Error)",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Aggregate & Analyze Prices": {
          "main": [
            [
              {
                "node": "Format Email Report",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Parse & Validate Flight Request": {
          "main": [
            [
              {
                "node": "Check If Request Valid",
                "type": "main",
                "index": 0
              }
            ]
          ]
        },
        "Webhook - Receive Flight Request": {
          "main": [
            [
              {
                "node": "Parse & Validate Flight Request",
                "type": "main",
                "index": 0
              }
            ]
          ]
        }
      }
    },
    "lastUpdatedBy": 1,
    "workflowInfo": {
      "nodeCount": 19,
      "nodeTypes": {
        "n8n-nodes-base.if": {
          "count": 1
        },
        "n8n-nodes-base.ssh": {
          "count": 4
        },
        "n8n-nodes-base.code": {
          "count": 3
        },
        "n8n-nodes-base.webhook": {
          "count": 1
        },
        "n8n-nodes-base.emailSend": {
          "count": 1
        },
        "n8n-nodes-base.stickyNote": {
          "count": 7
        },
        "n8n-nodes-base.respondToWebhook": {
          "count": 2
        }
      }
    },
    "status": "published",
    "user": {
      "name": "Oneclick AI Squad",
      "username": "oneclick-ai",
      "bio": "The AI Squad Initiative is a pioneering effort to build, automate and scale AI-powered workflows using n8n.io. Our mission is to help individuals and businesses integrate AI agents seamlessly into their daily operations  from automating tasks and enhancing productivity to creating innovative, intelligent solutions. We design modular, reusable AI workflow templates that empower creators, developers and teams to supercharge their automation with minimal effort and maximum impact.",
      "verified": true,
      "links": [
        "https://www.oneclickitsolution.com/"
      ],
      "avatar": "https://gravatar.com/avatar/848fca91367142f65f9e5c55d64e5c9952b160d7b060d103b52aa343c6bc7b3d?r=pg&d=retro&size=200"
    },
    "nodes": [
      {
        "id": 11,
        "icon": "fa:envelope",
        "name": "n8n-nodes-base.emailSend",
        "codex": {
          "data": {
            "alias": [
              "SMTP",
              "email",
              "human",
              "form",
              "wait",
              "hitl",
              "approval"
            ],
            "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/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"
                }
              ],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.sendemail/"
                }
              ],
              "credentialDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/credentials/sendemail/"
                }
              ]
            },
            "categories": [
              "Communication",
              "HITL",
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "HITL": [
                "Human in the Loop"
              ]
            }
          }
        },
        "group": "[\"output\"]",
        "defaults": {
          "name": "Send Email",
          "color": "#00bb88"
        },
        "iconData": {
          "icon": "envelope",
          "type": "icon"
        },
        "displayName": "Send Email",
        "typeVersion": 2,
        "nodeCategories": [
          {
            "id": 6,
            "name": "Communication"
          },
          {
            "id": 9,
            "name": "Core Nodes"
          },
          {
            "id": 28,
            "name": "HITL"
          }
        ]
      },
      {
        "id": 20,
        "icon": "fa:map-signs",
        "name": "n8n-nodes-base.if",
        "codex": {
          "data": {
            "alias": [
              "Router",
              "Filter",
              "Condition",
              "Logic",
              "Boolean",
              "Branch"
            ],
            "details": "The IF node can be used to implement binary conditional logic in your workflow. You can set up one-to-many conditions to evaluate each item of data being inputted into the node. That data will either evaluate to TRUE or FALSE and route out of the node accordingly.\n\nThis node has multiple types of conditions: Bool, String, Number, and Date & Time.",
            "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/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/create-a-toxic-language-detector-for-telegram/",
                  "icon": "🤬",
                  "label": "Create a toxic language detector for Telegram in 4 step"
                },
                {
                  "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/automation-for-maintainers-of-open-source-projects/",
                  "icon": "🏷️",
                  "label": "How to automatically manage contributions to open-source projects"
                },
                {
                  "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/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/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-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/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.if/"
                }
              ]
            },
            "categories": [
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Flow"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "If",
          "color": "#408000"
        },
        "iconData": {
          "icon": "map-signs",
          "type": "icon"
        },
        "displayName": "If",
        "typeVersion": 2,
        "nodeCategories": [
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 47,
        "icon": "file:webhook.svg",
        "name": "n8n-nodes-base.webhook",
        "codex": {
          "data": {
            "alias": [
              "HTTP",
              "API",
              "Build",
              "WH"
            ],
            "resources": {
              "generic": [
                {
                  "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/running-n8n-on-ships-an-interview-with-maranics/",
                  "icon": "🛳",
                  "label": "Running n8n on ships: An interview with Maranics"
                },
                {
                  "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/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/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/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/how-to-automatically-give-kudos-to-contributors-with-github-slack-and-n8n/",
                  "icon": "👏",
                  "label": "How to automatically give kudos to contributors with GitHub, Slack, 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/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/creating-custom-incident-response-workflows-with-n8n/",
                  "label": "How to automate every step of an incident response workflow"
                },
                {
                  "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/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-goomer-automated-their-operations-with-over-200-n8n-workflows/",
                  "icon": "🛵",
                  "label": "How Goomer automated their operations with over 200 n8n workflows"
                }
              ],
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/"
                }
              ]
            },
            "categories": [
              "Development",
              "Core Nodes"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Helpers"
              ]
            }
          }
        },
        "group": "[\"trigger\"]",
        "defaults": {
          "name": "Webhook"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTM1IDM3Yy0yLjIgMC00LTEuOC00LTRzMS44LTQgNC00IDQgMS44IDQgNC0xLjggNC00IDQiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUgNDNjLTMgMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEgMS40IDIuOSAyLjMgNC43IDIuMyAzLjMgMCA2LTIuNyA2LTZzLTIuNy02LTYtNmMtMSAwLTIgLjMtMi45LjdsLTEuNyAxTDIzLjMgMTZsMy41LTEuOSA1LjMgOS40YzEtLjMgMi0uNSAzLS41IDUuNSAwIDEwIDQuNSAxMCAxMFM0MC41IDQzIDM1IDQzIi8+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTE0IDQzQzguNSA0MyA0IDM4LjUgNCAzM2MwLTQuNiAzLjEtOC41IDcuNS05LjdsMSAzLjlDOS45IDI3LjkgOCAzMC4zIDggMzNjMCAzLjMgMi43IDYgNiA2czYtMi43IDYtNnYtMmgxNXY0SDIzLjhjLS45IDQuNi01IDgtOS44IDgiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTQgMzdjLTIuMiAwLTQtMS44LTQtNHMxLjgtNCA0LTQgNCAxLjggNCA0LTEuOCA0LTQgNCIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSAxOWMtMi4yIDAtNC0xLjgtNC00czEuOC00IDQtNCA0IDEuOCA0IDQtMS44IDQtNCA0Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0ibTE1LjcgMzQtMy40LTIgNS45LTkuN2MtMi0xLjktMy4yLTQuNS0zLjItNy4zIDAtNS41IDQuNS0xMCAxMC0xMHMxMCA0LjUgMTAgMTBjMCAuOS0uMSAxLjctLjMgMi41bC0zLjktMWMuMS0uNS4yLTEgLjItMS41IDAtMy4zLTIuNy02LTYtNnMtNiAyLjctNiA2YzAgMi4xIDEuMSA0IDIuOSA1LjFsMS43IDF6Ii8+PC9zdmc+"
        },
        "displayName": "Webhook",
        "typeVersion": 2,
        "nodeCategories": [
          {
            "id": 5,
            "name": "Development"
          },
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 490,
        "icon": "fa:terminal",
        "name": "n8n-nodes-base.ssh",
        "codex": {
          "data": {
            "alias": [
              "remote"
            ],
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.ssh/"
                }
              ]
            },
            "categories": [
              "Core Nodes",
              "Development"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": [
              "Helpers"
            ]
          }
        },
        "group": "[\"input\"]",
        "defaults": {
          "name": "SSH",
          "color": "#000000"
        },
        "iconData": {
          "icon": "terminal",
          "type": "icon"
        },
        "displayName": "SSH",
        "typeVersion": 1,
        "nodeCategories": [
          {
            "id": 5,
            "name": "Development"
          },
          {
            "id": 9,
            "name": "Core Nodes"
          }
        ]
      },
      {
        "id": 535,
        "icon": "file:webhook.svg",
        "name": "n8n-nodes-base.respondToWebhook",
        "codex": {
          "data": {
            "resources": {
              "primaryDocumentation": [
                {
                  "url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.respondtowebhook/"
                }
              ]
            },
            "categories": [
              "Core Nodes",
              "Utility"
            ],
            "nodeVersion": "1.0",
            "codexVersion": "1.0",
            "subcategories": {
              "Core Nodes": [
                "Helpers"
              ]
            }
          }
        },
        "group": "[\"transform\"]",
        "defaults": {
          "name": "Respond to Webhook"
        },
        "iconData": {
          "type": "file",
          "fileBuffer": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCI+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTM1IDM3Yy0yLjIgMC00LTEuOC00LTRzMS44LTQgNC00IDQgMS44IDQgNC0xLjggNC00IDQiLz48cGF0aCBmaWxsPSIjMzc0NzRmIiBkPSJNMzUgNDNjLTMgMC01LjktMS40LTcuOC0zLjdsMy4xLTIuNWMxLjEgMS40IDIuOSAyLjMgNC43IDIuMyAzLjMgMCA2LTIuNyA2LTZzLTIuNy02LTYtNmMtMSAwLTIgLjMtMi45LjdsLTEuNyAxTDIzLjMgMTZsMy41LTEuOSA1LjMgOS40YzEtLjMgMi0uNSAzLS41IDUuNSAwIDEwIDQuNSAxMCAxMFM0MC41IDQzIDM1IDQzIi8+PHBhdGggZmlsbD0iIzM3NDc0ZiIgZD0iTTE0IDQzQzguNSA0MyA0IDM4LjUgNCAzM2MwLTQuNiAzLjEtOC41IDcuNS05LjdsMSAzLjlDOS45IDI3LjkgOCAzMC4zIDggMzNjMCAzLjMgMi43IDYgNiA2czYtMi43IDYtNnYtMmgxNXY0SDIzLjhjLS45IDQuNi01IDgtOS44IDgiLz48cGF0aCBmaWxsPSIjZTkxZTYzIiBkPSJNMTQgMzdjLTIuMiAwLTQtMS44LTQtNHMxLjgtNCA0LTQgNCAxLjggNCA0LTEuOCA0LTQgNCIvPjxwYXRoIGZpbGw9IiMzNzQ3NGYiIGQ9Ik0yNSAxOWMtMi4yIDAtNC0xLjgtNC00czEuOC00IDQtNCA0IDEuOCA0IDQtMS44IDQtNCA0Ii8+PHBhdGggZmlsbD0iI2U5MWU2MyIgZD0ibTE1LjcgMzQtMy40LTIgNS45LTkuN2MtMi0xLjktMy4yLTQuNS0zLjItNy4zIDAtNS41IDQuNS0xMCAxMC0xMHMxMCA0LjUgMTAgMTBjMCAuOS0uMSAxLjctLjMgMi41bC0zLjktMWMuMS0uNS4yLTEgLjItMS41IDAtMy4zLTIuNy02LTYtNnMtNiAyLjctNiA2YzAgMi4xIDEuMSA0IDIuOSA1LjFsMS43IDF6Ii8+PC9zdmc+"
        },
        "displayName": "Respond to Webhook",
        "typeVersion": 2,
        "nodeCategories": [
          {
            "id": 7,
            "name": "Utility"
          },
          {
            "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"
          }
        ]
      }
    ],
    "categories": [
      {
        "id": 45,
        "name": "Miscellaneous"
      }
    ],
    "image": []
  }
}