{
  "id": "Yi6ZHj09hQ6aoCt2",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Qwen3-VL-8B-Thinking Travel Planner: Search-->Compare-->Email Best Itineraries",
  "tags": [],
  "nodes": [
    {
      "id": "f3a23f51-4039-4a08-bad5-d9e1f475e8af",
      "name": "Webhook - Travel Request",
      "type": "n8n-nodes-base.webhook",
      "position": [
        2832,
        -256
      ],
      "parameters": {
        "path": "travel-search",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "65e7017e-5242-4875-bf24-661fc68eb771",
      "name": "Extract Request Data",
      "type": "n8n-nodes-base.set",
      "position": [
        2992,
        -256
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "destination",
              "type": "string",
              "value": "={{ $json.body.destination || 'Shanghai' }}"
            },
            {
              "id": "a2",
              "name": "departureCity",
              "type": "string",
              "value": "={{ $json.body.departureCity }}"
            },
            {
              "id": "a3",
              "name": "checkInDate",
              "type": "string",
              "value": "={{ $json.body.checkInDate }}"
            },
            {
              "id": "a4",
              "name": "checkOutDate",
              "type": "string",
              "value": "={{ $json.body.checkOutDate }}"
            },
            {
              "id": "a5",
              "name": "travelers",
              "type": "number",
              "value": "={{ $json.body.travelers || 1 }}"
            },
            {
              "id": "a6",
              "name": "email",
              "type": "string",
              "value": "={{ $json.body.email }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "51891077-df8a-45db-b575-0585f52aa1db",
      "name": "Search Flights - Skyscanner",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -640
      ],
      "parameters": {
        "url": "https://skyscanner-api.p.rapidapi.com/v3/flights/live/search/create",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "={{ {\"market\":\"US\",\"locale\":\"en-US\",\"currency\":\"USD\",\"queryLegs\":[{\"originPlace\":{\"queryPlace\":{\"iata\":$json.departureCity}},\"destinationPlace\":{\"queryPlace\":{\"iata\":$json.destination}},\"date\":{\"year\":$json.checkInDate.split('-')[0],\"month\":$json.checkInDate.split('-')[1],\"day\":$json.checkInDate.split('-')[2]}}],\"adults\":$json.travelers,\"cabinClass\":\"CABIN_CLASS_ECONOMY\"} }}"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "X-RapidAPI-Key",
              "value": "={{ $credentials.rapidApiKey }}"
            },
            {
              "name": "X-RapidAPI-Host",
              "value": "skyscanner-api.p.rapidapi.com"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "953e3d0b-528d-41ba-b7ba-4fc7363c703f",
      "name": "Search Hotels - Booking.com",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -448
      ],
      "parameters": {
        "url": "https://booking-com.p.rapidapi.com/v1/hotels/search",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "dest_type",
              "value": "city"
            },
            {
              "name": "dest_id",
              "value": "={{ $('Extract Request Data').item.json.destination }}"
            },
            {
              "name": "checkin_date",
              "value": "={{ $('Extract Request Data').item.json.checkInDate }}"
            },
            {
              "name": "checkout_date",
              "value": "={{ $('Extract Request Data').item.json.checkOutDate }}"
            },
            {
              "name": "adults_number",
              "value": "={{ $('Extract Request Data').item.json.travelers }}"
            },
            {
              "name": "order_by",
              "value": "price"
            },
            {
              "name": "units",
              "value": "metric"
            },
            {
              "name": "room_number",
              "value": "1"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "X-RapidAPI-Key",
              "value": "={{ $credentials.rapidApiKey }}"
            },
            {
              "name": "X-RapidAPI-Host",
              "value": "booking-com.p.rapidapi.com"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "133388c8-6fe6-42cb-94b7-f76bf0cb4b2e",
      "name": "Send Email via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        4480,
        64
      ],
      "parameters": {
        "sendTo": "={{ $('Webhook - Travel Request').item.json.body.email }}",
        "message": "={{ $json.html }}",
        "options": {},
        "subject": "\ud83e\udd16 AI-Optimized Travel Itinerary: {{ $('Extract Request Data').item.json.destination }} Trip"
      },
      "typeVersion": 2.1
    },
    {
      "id": "5fcf6271-c0fd-430b-858e-4a462e98b648",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        4784,
        64
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { \"success\": true, \"message\": \"AI-optimized travel itinerary sent successfully!\", \"itinerariesCount\": $('AI Score & Recommendations').all().length, \"email\": $('Extract Request Data').item.json.email, \"bestDeal\": { \"price\": $('AI Score & Recommendations').first().json.totalPrice, \"aiScore\": $('AI Score & Recommendations').first().json.aiScore } } }}"
      },
      "typeVersion": 1
    },
    {
      "id": "2aedf5b8-536b-44bc-9b6d-3b80daa2c6c0",
      "name": "AI Agent - Itinerary Optimizer",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        3808,
        -496
      ],
      "parameters": {
        "options": {
          "systemMessage": "You are a travel expert. Analyze the provided flight and hotel combinations and provide a detailed recommendation score (0-100) for each itinerary based on:\n1. Total price value\n2. Flight convenience (stops, duration, departure times)\n3. Hotel quality (rating, reviews, location)\n4. Overall trip experience\n\nFor each itinerary, return:\n- score (0-100)\n- reasoning (2-3 sentences)\n- highlights (bullet points)\n- warnings (if any)\n\nFormat as JSON array."
        }
      },
      "typeVersion": 1
    },
    {
      "id": "759f481f-598a-4bff-a3d1-3cb3310ecb88",
      "name": "Search Alternative Flights - Kiwi",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -256
      ],
      "parameters": {
        "url": "https://api.tequila.kiwi.com/v2/search",
        "options": {},
        "authentication": "predefinedCredentialType"
      },
      "typeVersion": 1
    },
    {
      "id": "63c46681-a628-4bd8-aab0-a226ab57e703",
      "name": "Get Weather Forecast",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        -64
      ],
      "parameters": {
        "url": "https://api.openweathermap.org/data/2.5/forecast",
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "15404c5f-d821-4455-abf6-a08db26baee3",
      "name": "Search Local Activities - Viator",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3216,
        128
      ],
      "parameters": {
        "url": "https://viator-api.p.rapidapi.com/search",
        "options": {},
        "authentication": "predefinedCredentialType"
      },
      "typeVersion": 1
    },
    {
      "id": "27b5a21e-02a6-43e2-b1b9-a7bd5074dd29",
      "name": "Merge All Data Sources",
      "type": "n8n-nodes-base.code",
      "position": [
        3440,
        -256
      ],
      "parameters": {
        "jsCode": "const skyscannerFlights = $input.item(0).json;\nconst kiwiFlights = $input.item(1).json;\nconst bookingHotels = $input.item(2).json;\nconst weather = $input.item(3).json;\nconst activities = $input.item(4).json;\nconst requestData = $('Extract Request Data').first().json;\n\nconst allFlightData = {\n  skyscanner: skyscannerFlights,\n  kiwi: kiwiFlights,\n  weather: weather,\n  activities: activities\n};\n\nconst hotelData = bookingHotels;\n\nreturn [{\n  json: {\n    flights: allFlightData,\n    hotels: hotelData,\n    request: requestData\n  }\n}];"
      },
      "typeVersion": 1
    },
    {
      "id": "d460fa9d-9991-4f29-befc-caff18a9cf38",
      "name": "Enhanced Itinerary Builder",
      "type": "n8n-nodes-base.code",
      "position": [
        3616,
        -256
      ],
      "parameters": {
        "jsCode": "const mergedData = $input.first().json;\nconst requestData = mergedData.request;\n\n// Parse Skyscanner flights\nconst skyscannerFlights = [];\nif (mergedData.flights.skyscanner.itineraries?.results) {\n  mergedData.flights.skyscanner.itineraries.results.slice(0, 8).forEach((flight, i) => {\n    skyscannerFlights.push({\n      id: `sky_${i}`,\n      source: 'Skyscanner',\n      airline: flight.legs[0]?.carriers?.marketing[0]?.name || 'Unknown',\n      departureTime: flight.legs[0]?.departure || '',\n      arrivalTime: flight.legs[0]?.arrival || '',\n      duration: flight.legs[0]?.duration || 0,\n      stops: flight.legs[0]?.stopCount || 0,\n      price: flight.pricing?.price?.amount || 0,\n      currency: flight.pricing?.price?.currency || 'USD',\n      bookingLink: flight.deeplink || '#'\n    });\n  });\n}\n\n// Parse Kiwi flights\nconst kiwiFlights = [];\nif (mergedData.flights.kiwi.data) {\n  mergedData.flights.kiwi.data.slice(0, 8).forEach((flight, i) => {\n    kiwiFlights.push({\n      id: `kiwi_${i}`,\n      source: 'Kiwi.com',\n      airline: flight.airlines?.[0] || 'Unknown',\n      departureTime: new Date(flight.local_departure).toISOString(),\n      arrivalTime: new Date(flight.local_arrival).toISOString(),\n      duration: flight.duration?.total || 0,\n      stops: (flight.route?.length || 1) - 1,\n      price: flight.price || 0,\n      currency: flight.currency || 'USD',\n      bookingLink: flight.deep_link || '#'\n    });\n  });\n}\n\nconst allFlights = [...skyscannerFlights, ...kiwiFlights].sort((a,b) => a.price - b.price);\n\n// Parse hotels\nconst hotels = [];\nif (mergedData.hotels.result) {\n  mergedData.hotels.result.slice(0, 10).forEach((hotel, i) => {\n    hotels.push({\n      id: `hotel_${i}`,\n      name: hotel.hotel_name || 'Unknown',\n      address: hotel.address || '',\n      rating: hotel.review_score || 0,\n      reviewCount: hotel.review_nr || 0,\n      price: hotel.min_total_price || 0,\n      pricePerNight: hotel.price_breakdown?.gross_price || 0,\n      currency: hotel.currency_code || 'USD',\n      amenities: hotel.unit_configuration_label || '',\n      distanceToCenter: hotel.distance || '',\n      bookingLink: hotel.url || '#'\n    });\n  });\n}\n\n// Weather summary\nlet weatherSummary = 'No weather data';\nif (mergedData.flights.weather?.list) {\n  const temps = mergedData.flights.weather.list.slice(0, 8).map(w => w.main.temp);\n  const avgTemp = (temps.reduce((a,b)=>a+b,0)/temps.length).toFixed(1);\n  const conditions = mergedData.flights.weather.list[0]?.weather[0]?.description || 'N/A';\n  weatherSummary = `${avgTemp}\u00b0C, ${conditions}`;\n}\n\n// Activities\nconst activities = [];\nif (mergedData.flights.activities?.data) {\n  mergedData.flights.activities.data.slice(0, 10).forEach((act, i) => {\n    activities.push({\n      id: `act_${i}`,\n      name: act.title || 'Activity',\n      description: act.description || '',\n      rating: act.rating || 0,\n      reviewCount: act.reviewCount || 0,\n      price: act.price?.amount || 0,\n      currency: act.price?.currency || 'USD',\n      duration: act.duration || '',\n      bookingLink: act.productUrl || '#'\n    });\n  });\n}\n\n// Create enhanced itineraries (top 8)\nconst itineraries = [];\nfor (let f = 0; f < Math.min(4, allFlights.length); f++) {\n  for (let h = 0; h < Math.min(2, hotels.length); h++) {\n    if (itineraries.length >= 8) break;\n    \n    const flight = allFlights[f];\n    const hotel = hotels[h];\n    const selectedActivities = activities.slice(0, 3);\n    \n    const totalPrice = flight.price + hotel.price + selectedActivities.reduce((sum, act) => sum + act.price, 0);\n    \n    itineraries.push({\n      id: `itinerary_${itineraries.length + 1}`,\n      flight: flight,\n      hotel: hotel,\n      activities: selectedActivities,\n      weather: weatherSummary,\n      totalPrice: totalPrice,\n      currency: flight.currency,\n      destination: requestData.destination,\n      departureCity: requestData.departureCity,\n      checkInDate: requestData.checkInDate,\n      checkOutDate: requestData.checkOutDate,\n      travelers: requestData.travelers\n    });\n  }\n  if (itineraries.length >= 8) break;\n}\n\nitineraries.sort((a, b) => a.totalPrice - b.totalPrice);\n\nreturn itineraries.slice(0, 8).map(it => ({ json: it }));"
      },
      "typeVersion": 1
    },
    {
      "id": "33047f44-9726-4f2f-a029-c7e71c8aceb8",
      "name": "AI Score & Recommendations",
      "type": "n8n-nodes-base.code",
      "position": [
        4128,
        64
      ],
      "parameters": {
        "jsCode": "const itineraries = $input.all().map(item => item.json);\nconst aiAnalysis = $('AI Agent - Itinerary Optimizer').first().json;\n\nlet aiScores = [];\ntry {\n  aiScores = typeof aiAnalysis.output === 'string' ? JSON.parse(aiAnalysis.output) : aiAnalysis.output;\n} catch (e) {\n  aiScores = itineraries.map((_, i) => ({\n    score: 85 - i * 5,\n    reasoning: 'AI analysis pending',\n    highlights: ['Competitive pricing', 'Good availability'],\n    warnings: []\n  }));\n}\n\nconst enrichedItineraries = itineraries.map((itinerary, index) => {\n  const aiData = aiScores[index] || { score: 75, reasoning: 'Standard option', highlights: [], warnings: [] };\n  return {\n    ...itinerary,\n    aiScore: aiData.score || 75,\n    aiReasoning: aiData.reasoning || 'Good option',\n    aiHighlights: aiData.highlights || [],\n    aiWarnings: aiData.warnings || []\n  };\n});\n\nenrichedItineraries.sort((a, b) => b.aiScore - a.aiScore);\n\nreturn enrichedItineraries.map(it => ({ json: it }));"
      },
      "typeVersion": 1
    },
    {
      "id": "015b10c8-ad0a-4f34-8855-e1366cde7658",
      "name": "Generate Premium HTML Email",
      "type": "n8n-nodes-base.code",
      "position": [
        4288,
        64
      ],
      "parameters": {
        "jsCode": "const itineraries = $input.all().map(item => item.json);\n\nif (itineraries.length === 0) {\n  return [{ json: { html: '<html><body><h2>No Results</h2></body></html>' } }];\n}\n\nconst first = itineraries[0];\nlet html = `\n<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"UTF-8\">\n<style>\n  body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #1a1a1a; max-width: 1000px; margin: 0 auto; padding: 20px; background: #f8f9fa; }\n  .container { background: white; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }\n  .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px 30px; text-align: center; }\n  .header h1 { margin: 0; font-size: 32px; font-weight: 700; }\n  .summary { background: #f0f4ff; padding: 25px 30px; border-left: 5px solid #667eea; margin: 20px; }\n  .summary strong { color: #667eea; }\n  .itinerary { border: 2px solid #e0e0e0; margin: 20px; border-radius: 10px; overflow: hidden; transition: transform 0.2s; }\n  .itinerary:hover { transform: translateY(-3px); box-shadow: 0 6px 20px rgba(0,0,0,0.15); }\n  .best-deal { border-color: #ffd700; background: linear-gradient(to right, #fff9e6, #ffffff); }\n  .best-deal .itinerary-header { background: linear-gradient(135deg, #ffd700, #ffed4e); }\n  .itinerary-header { background: #667eea; color: white; padding: 20px; display: flex; justify-content: space-between; align-items: center; }\n  .ai-score { background: white; color: #667eea; padding: 8px 20px; border-radius: 20px; font-weight: bold; font-size: 18px; }\n  .section { padding: 25px; }\n  .section-title { color: #667eea; font-size: 20px; font-weight: 600; margin-bottom: 15px; border-bottom: 2px solid #e0e0e0; padding-bottom: 8px; }\n  .detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 15px 0; }\n  .detail-item { padding: 12px; background: #f8f9fa; border-radius: 6px; }\n  .detail-label { color: #666; font-size: 13px; margin-bottom: 5px; }\n  .detail-value { color: #1a1a1a; font-weight: 600; font-size: 15px; }\n  .activities-list { list-style: none; padding: 0; }\n  .activities-list li { padding: 12px; margin: 8px 0; background: #f0f4ff; border-left: 4px solid #667eea; border-radius: 4px; }\n  .ai-insights { background: linear-gradient(135deg, #e0f7fa, #f0f4ff); padding: 20px; border-radius: 8px; margin: 15px 0; }\n  .highlight { color: #2e7d32; }\n  .warning { color: #d32f2f; }\n  .price-box { text-align: center; background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 25px; border-radius: 8px; margin: 20px 0; }\n  .price { font-size: 42px; font-weight: 700; }\n  .button { display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; text-decoration: none; border-radius: 25px; margin: 8px; font-weight: 600; transition: all 0.3s; }\n  .button:hover { transform: scale(1.05); box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); }\n  .comparison-table { width: 100%; border-collapse: collapse; margin: 20px 0; }\n  .comparison-table th { background: #667eea; color: white; padding: 15px; text-align: left; font-weight: 600; }\n  .comparison-table td { padding: 12px; border-bottom: 1px solid #e0e0e0; }\n  .comparison-table tr:hover { background: #f8f9fa; }\n  .badge { display: inline-block; padding: 5px 12px; background: #ffd700; color: #1a1a1a; border-radius: 15px; font-size: 12px; font-weight: 700; margin-left: 10px; }\n  .weather-box { background: linear-gradient(135deg, #4facfe, #00f2fe); color: white; padding: 15px; border-radius: 8px; text-align: center; margin: 15px 0; }\n</style>\n</head>\n<body>\n<div class=\"container\">\n  <div class=\"header\">\n    <h1>\u2708\ufe0f Your AI-Optimized Travel Itineraries</h1>\n    <p style=\"margin: 10px 0 0 0; font-size: 18px; opacity: 0.9;\">Powered by Advanced AI Analysis</p>\n  </div>\n\n  <div class=\"summary\">\n    <strong>\ud83d\udccd Route:</strong> ${first.departureCity} \u2192 ${first.destination}<br>\n    <strong>\ud83d\udcc5 Dates:</strong> ${first.checkInDate} to ${first.checkOutDate}<br>\n    <strong>\ud83d\udc65 Travelers:</strong> ${first.travelers}<br>\n    <strong>\ud83c\udf24\ufe0f Weather:</strong> ${first.weather}\n  </div>\n\n  <div style=\"padding: 30px;\">\n    <h2 style=\"color: #667eea; font-size: 28px; margin-bottom: 20px;\">\ud83c\udfc6 Top ${itineraries.length} AI-Ranked Itineraries</h2>\n`;\n\nitineraries.forEach((it, index) => {\n  const isBest = index === 0;\n  html += `\n    <div class=\"itinerary ${isBest ? 'best-deal' : ''}\">\n      <div class=\"itinerary-header\" ${isBest ? 'style=\"background: linear-gradient(135deg, #ffd700, #ffed4e); color: #1a1a1a;\"' : ''}>\n        <div>\n          <h3 style=\"margin: 0; font-size: 24px;\">Option ${index + 1} ${isBest ? '<span class=\"badge\">BEST VALUE</span>' : ''}</h3>\n          <p style=\"margin: 5px 0 0 0; opacity: 0.8;\">AI-Optimized Package</p>\n        </div>\n        <div class=\"ai-score\">\ud83e\udd16 ${it.aiScore}/100</div>\n      </div>\n\n      <div class=\"section\">\n        <div class=\"ai-insights\">\n          <strong style=\"color: #667eea; font-size: 16px;\">\ud83e\udde0 AI Analysis:</strong>\n          <p style=\"margin: 10px 0;\">${it.aiReasoning}</p>\n          ${it.aiHighlights.length > 0 ? `<div class=\"highlight\">\u2713 ${it.aiHighlights.join(' \u2022 ')}</div>` : ''}\n          ${it.aiWarnings.length > 0 ? `<div class=\"warning\">\u26a0 ${it.aiWarnings.join(' \u2022 ')}</div>` : ''}\n        </div>\n\n        <div class=\"section-title\">\u2708\ufe0f Flight Details</div>\n        <div class=\"detail-grid\">\n          <div class=\"detail-item\"><div class=\"detail-label\">Source</div><div class=\"detail-value\">${it.flight.source}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Airline</div><div class=\"detail-value\">${it.flight.airline}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Departure</div><div class=\"detail-value\">${it.flight.departureTime}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Arrival</div><div class=\"detail-value\">${it.flight.arrivalTime}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Stops</div><div class=\"detail-value\">${it.flight.stops === 0 ? 'Non-stop \u2b50' : it.flight.stops + ' stop(s)'}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Flight Price</div><div class=\"detail-value\">${it.currency} $${it.flight.price.toFixed(2)}</div></div>\n        </div>\n\n        <div class=\"section-title\">\ud83c\udfe8 Hotel Details</div>\n        <div class=\"detail-grid\">\n          <div class=\"detail-item\"><div class=\"detail-label\">Hotel Name</div><div class=\"detail-value\">${it.hotel.name}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Rating</div><div class=\"detail-value\">\u2b50 ${it.hotel.rating}/10 (${it.hotel.reviewCount} reviews)</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Location</div><div class=\"detail-value\">${it.hotel.distanceToCenter || 'City Center'}</div></div>\n          <div class=\"detail-item\"><div class=\"detail-label\">Hotel Price</div><div class=\"detail-value\">${it.currency} $${it.hotel.price.toFixed(2)}</div></div>\n        </div>\n\n        ${it.activities.length > 0 ? `\n        <div class=\"section-title\">\ud83c\udfaf Recommended Activities</div>\n        <ul class=\"activities-list\">\n          ${it.activities.map(act => `\n            <li>\n              <strong>${act.name}</strong> ${act.rating > 0 ? `(${act.rating}\u2b50)` : ''}<br>\n              <span style=\"color: #666; font-size: 13px;\">${act.description.substring(0, 100)}...</span><br>\n              <strong style=\"color: #667eea;\">${act.currency} $${act.price.toFixed(2)}</strong> \u2022 ${act.duration}\n            </li>\n          `).join('')}\n        </ul>\n        ` : ''}\n\n        <div class=\"weather-box\">\n          <strong>\ud83c\udf24\ufe0f Weather Forecast:</strong> ${it.weather}\n        </div>\n\n        <div class=\"price-box\">\n          <div style=\"font-size: 16px; margin-bottom: 5px;\">TOTAL PACKAGE PRICE</div>\n          <div class=\"price\">${it.currency} $${it.totalPrice.toFixed(2)}</div>\n          <div style=\"margin-top: 20px;\">\n            <a href=\"${it.flight.bookingLink}\" class=\"button\">Book Flight</a>\n            <a href=\"${it.hotel.bookingLink}\" class=\"button\">Book Hotel</a>\n          </div>\n        </div>\n      </div>\n    </div>\n  `;\n});\n\nhtml += `\n    <div style=\"margin-top: 40px;\">\n      <h2 style=\"color: #667eea; font-size: 24px;\">\ud83d\udcca Price Comparison Matrix</h2>\n      <table class=\"comparison-table\">\n        <tr>\n          <th>Option</th>\n          <th>AI Score</th>\n          <th>Source</th>\n          <th>Flight</th>\n          <th>Hotel</th>\n          <th>Activities</th>\n          <th>Total</th>\n        </tr>\n`;\n\nitineraries.forEach((it, i) => {\n  const actPrice = it.activities.reduce((sum, a) => sum + a.price, 0);\n  html += `\n    <tr ${i === 0 ? 'style=\"background: #fff9e6; font-weight: bold;\"' : ''}>\n      <td>Option ${i + 1} ${i === 0 ? '<span class=\"badge\">BEST</span>' : ''}</td>\n      <td>${it.aiScore}/100</td>\n      <td>${it.flight.source}</td>\n      <td>$${it.flight.price.toFixed(2)}</td>\n      <td>$${it.hotel.price.toFixed(2)}</td>\n      <td>$${actPrice.toFixed(2)}</td>\n      <td style=\"color: #667eea; font-size: 18px; font-weight: bold;\">$${it.totalPrice.toFixed(2)}</td>\n    </tr>\n  `;\n});\n\nhtml += `\n      </table>\n    </div>\n\n    <div style=\"margin-top: 40px; padding: 25px; background: linear-gradient(135deg, #f0f4ff, #e0f7fa); border-radius: 10px;\">\n      <h3 style=\"color: #667eea; margin-top: 0;\">\ud83d\udca1 AI Travel Tips</h3>\n      <ul style=\"line-height: 1.8;\">\n        <li>Book flights and hotels separately for maximum flexibility</li>\n        <li>Weather data is forecast-based; pack accordingly</li>\n        <li>Activities can be booked on-site or in advance</li>\n        <li>Prices are dynamic and subject to change</li>\n        <li>Consider travel insurance for peace of mind</li>\n      </ul>\n    </div>\n  </div>\n</div>\n\n<div style=\"text-align: center; margin-top: 30px; padding: 20px; color: #666; font-size: 13px;\">\n  <p>\ud83e\udd16 This itinerary was generated by AI-powered n8n workflow automation<br>\n  Prices and availability are subject to change \u2022 Book early for best rates</p>\n</div>\n</body>\n</html>\n`;\n\nreturn [{ json: { html, itineraries } }];"
      },
      "typeVersion": 1
    },
    {
      "id": "0551345f-ca44-4adc-8f49-5ddf522f8a74",
      "name": "Send Slack Notification",
      "type": "n8n-nodes-base.slack",
      "position": [
        4656,
        256
      ],
      "parameters": {
        "text": "\ud83c\udf89 New travel itinerary sent!\\n\u2708\ufe0f Route: {{ $('Extract Request Data').item.json.departureCity }} \u2192 {{ $('Extract Request Data').item.json.destination }}\\n\ud83d\udce7 Email: {{ $('Webhook - Travel Request').item.json.body.email }}\\n\ud83c\udfc6 Best Deal: ${{ $('AI Score & Recommendations').first().json.totalPrice.toFixed(2) }}\\n\ud83e\udd16 AI Score: {{ $('AI Score & Recommendations').first().json.aiScore }}/100",
        "channel": "#travel-bookings",
        "attachments": [],
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "typeVersion": 1
    },
    {
      "id": "82bb48ce-ad62-43cc-bb7e-69cf5f55e4c0",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3792,
        -320
      ],
      "parameters": {
        "model": "qwen/qwen3-vl-8b-thinking",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e66d7eb9-383f-4ec3-9e76-c33dcfabffdf",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2288,
        -608
      ],
      "parameters": {
        "width": 512,
        "height": 768,
        "content": "## Introduction\nAutomates travel planning by aggregating flights, hotels, activities, and weather via APIs, then uses AI to generate professional itineraries delivered through Gmail and Slack.\n\n## How It Works\nWebhook receives requests, searches APIs (Skyscanner, Booking.com, Kiwi, Viator, weather), merges data, AI builds itineraries, scores options, generates HTML emails, delivers via Gmail/Slack.\n\n## Workflow Template\nWebhook \u2192 Extract \u2192 Parallel Searches (Flights/Hotels/Activities/Weather) \u2192 Merge \u2192 Build Itinerary \u2192 AI Processing \u2192 Score \u2192 Generate HTML \u2192 Gmail \u2192 Slack \u2192 Response\n\n## Workflow Steps\n1. Trigger & Extract: Receives destination, dates, preferences, extracts parameters.\n2. Data Gathering: Parallel APIs fetch flights, hotels, activities, weather, merges responses.\n3. AI Processing: Analyzes data, creates itinerary, ranks recommendations.\n4. Delivery: Generates HTML email, sends via Gmail/Slack, confirms completion.\n\n## Setup Instructions\n1. API Configuration: Add keys for Skyscanner, Booking.com, Kiwi, Viator, OpenWeatherMap, OpenRouter.\n2. Communication: Connect Gmail OAuth2, Slack webhook.\n3. Customization: Adjust endpoints, AI prompts, HTML template, scoring criteria.\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "70771bd2-ff0c-46a1-86ae-08d5698b5c56",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4144,
        -544
      ],
      "parameters": {
        "color": 5,
        "width": 352,
        "height": 576,
        "content": "## Prerequisites\n- API keys: Skyscanner, Booking.com, Kiwi, Viator, OpenWeatherMap, OpenRouter\n- Gmail account\n- Slack workspace\n- n8n instance\n## Use Cases\n- Corporate travel planning\n- Vacation itinerary generation\n- Group trip coordination\n\n## Customization\n- Add sources (Airbnb, TripAdvisor)\n- Filter by budget preferences\n- Add PDF generation\n- Customize Slack format\n\n## Benefits\n- Saves 3-5 hours per trip\n- Real-time pricing aggregation\n- AI-powered personalization\n- Automated multi-channel delivery"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "973f16a5-77bb-4015-a5c5-a32efacdddd7",
  "connections": {
    "Generate HTML Email": {
      "main": [
        [
          {
            "node": "Send Email via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Request Data": {
      "main": [
        [
          {
            "node": "Search Flights - Skyscanner",
            "type": "main",
            "index": 0
          },
          {
            "node": "Search Hotels - Booking.com",
            "type": "main",
            "index": 0
          },
          {
            "node": "Search Alternative Flights - Kiwi",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Weather Forecast",
            "type": "main",
            "index": 0
          },
          {
            "node": "Search Local Activities - Viator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Weather Forecast": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Email via Gmail": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Slack Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent - Itinerary Optimizer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Data Sources": {
      "main": [
        [
          {
            "node": "Enhanced Itinerary Builder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Slack Notification": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Travel Request": {
      "main": [
        [
          {
            "node": "Extract Request Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Score & Recommendations": {
      "main": [
        [
          {
            "node": "Generate Premium HTML Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine & Rank Itineraries": {
      "main": [
        [
          {
            "node": "Generate HTML Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enhanced Itinerary Builder": {
      "main": [
        [
          {
            "node": "AI Agent - Itinerary Optimizer",
            "type": "main",
            "index": 0
          },
          {
            "node": "AI Score & Recommendations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Premium HTML Email": {
      "main": [
        [
          {
            "node": "Send Email via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Flights - Skyscanner": {
      "main": [
        [
          {
            "node": "Combine & Rank Itineraries",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Hotels - Booking.com": {
      "main": [
        [
          {
            "node": "Combine & Rank Itineraries",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent - Itinerary Optimizer": {
      "main": [
        [
          {
            "node": "AI Score & Recommendations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Local Activities - Viator": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Alternative Flights - Kiwi": {
      "main": [
        [
          {
            "node": "Merge All Data Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}