{
  "id": "sjh-_Xf1ekuPpodwk_WTV",
  "name": "Plan travel itineraries with AI using Apify, OpenAI, and Google Docs",
  "tags": [],
  "nodes": [
    {
      "id": "c82e7927-b801-4c02-bae7-cbfd2e7e1c4f",
      "name": "Travel Form",
      "type": "n8n-nodes-base.formTrigger",
      "position": [
        -896,
        416
      ],
      "parameters": {
        "options": {},
        "formTitle": "\u2708\ufe0f Travel Planner",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Departure Airport (IATA Code)",
              "placeholder": "e.g., JFK, LAX, LHR",
              "requiredField": true
            },
            {
              "fieldLabel": "Destination City",
              "placeholder": "e.g., Paris, Tokyo, Barcelona",
              "requiredField": true
            },
            {
              "fieldLabel": "Destination Airport (IATA Code)",
              "placeholder": "e.g., CDG, NRT, BCN",
              "requiredField": true
            },
            {
              "fieldType": "date",
              "fieldLabel": "Check-in Date",
              "requiredField": true
            },
            {
              "fieldType": "date",
              "fieldLabel": "Check-out Date",
              "requiredField": true
            },
            {
              "fieldType": "number",
              "fieldLabel": "Number of Travelers",
              "placeholder": "1",
              "requiredField": true
            }
          ]
        },
        "responseMode": "lastNode",
        "formDescription": "Plan your perfect trip! Enter your travel details below and we'll find the best flights, accommodations, restaurants, and attractions for you."
      },
      "typeVersion": 2.2
    },
    {
      "id": "de1f428d-c665-4016-84f9-cc9de570d228",
      "name": "Set Variables",
      "type": "n8n-nodes-base.set",
      "position": [
        -688,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "departure_airport",
              "name": "departureAirport",
              "type": "string",
              "value": "={{ $json['Departure Airport (IATA Code)'].toUpperCase() }}"
            },
            {
              "id": "destination_city",
              "name": "destination",
              "type": "string",
              "value": "={{ $json['Destination City'] }}"
            },
            {
              "id": "destination_airport",
              "name": "destinationAirport",
              "type": "string",
              "value": "={{ $json['Destination Airport (IATA Code)'].toUpperCase() }}"
            },
            {
              "id": "checkin_date",
              "name": "checkinDate",
              "type": "string",
              "value": "={{ $json['Check-in Date'] }}"
            },
            {
              "id": "checkout_date",
              "name": "checkoutDate",
              "type": "string",
              "value": "={{ $json['Check-out Date'] }}"
            },
            {
              "id": "travelers",
              "name": "travelers",
              "type": "number",
              "value": "={{ $json['Number of Travelers'] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0109421d-6ef5-41cf-85d1-b8f892ab59bb",
      "name": "Booking.com Scraper",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        -352,
        240
      ],
      "parameters": {
        "actorId": {
          "__rl": true,
          "mode": "list",
          "value": "oeiQgfg5fsmIJB7Cn",
          "cachedResultUrl": "https://console.apify.com/actors/oeiQgfg5fsmIJB7Cn/input",
          "cachedResultName": "Booking Scraper (voyager/booking-scraper)"
        },
        "customBody": "={\n  \"search\": \"{{ $json.destination }}\",\n  \"checkIn\": \"{{ $json.checkinDate }}\",\n  \"checkOut\": \"{{ $json.checkoutDate }}\",\n  \"rooms\": 1,\n  \"adults\": {{ $json.travelers }},\n  \"children\": 0,\n  \"currency\": \"USD\",\n  \"language\": \"en-us\",\n  \"maxItems\": 5,\n  \"sortBy\": \"bayesian_review_score\"\n}",
        "actorSource": "store"
      },
      "credentials": {
        "apifyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3b5b153e-5078-49c3-a22d-57bc3221e33a",
      "name": "HTTP Request Hotels",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        240
      ],
      "parameters": {
        "url": "=https://api.apify.com/v2/datasets/{{ $json.defaultDatasetId }}/items",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth"
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a16337c7-a806-4433-b00a-17603e7abe01",
      "name": "Google Flights Scraper",
      "type": "@apify/n8n-nodes-apify.apify",
      "position": [
        -352,
        480
      ],
      "parameters": {
        "actorId": {
          "__rl": true,
          "mode": "list",
          "value": "1dYHRKkEBHBPd0JM7",
          "cachedResultUrl": "https://console.apify.com/actors/1dYHRKkEBHBPd0JM7/input",
          "cachedResultName": "Google Flights Scraper (johnvc/Google-Flights-Data-Scraper-Flight-and-Price-Search)"
        },
        "customBody": "={\n  \"departure_id\": \"{{ $json.departureAirport }}\",\n  \"arrival_id\": \"{{ $json.destinationAirport }}\",\n  \"outbound_date\": \"{{ $json.checkinDate }}\",\n  \"return_date\": \"{{ $json.checkoutDate }}\",\n  \"adults\": {{ $json.travelers }},\n  \"travel_class\": 1,\n  \"currency\": \"USD\"\n}",
        "actorSource": "store"
      },
      "credentials": {
        "apifyApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ab18c15d-6213-41a5-a018-5e21d6cda637",
      "name": "HTTP Request Flights",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        480
      ],
      "parameters": {
        "url": "={{ $json.output.flightResults }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth"
      },
      "credentials": {
        "httpQueryAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "9a30f3b5-0dec-444a-a60c-b8f048ac4cad",
      "name": "AI Recommendations",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        -304,
        720
      ],
      "parameters": {
        "text": "=You are a travel expert. Please provide comprehensive travel recommendations for {{ $('Set Variables').item.json.destination }}.\n\nTravel dates: {{ $('Set Variables').item.json.checkinDate }} to {{ $('Set Variables').item.json.checkoutDate }}\nNumber of travelers: {{ $('Set Variables').item.json.travelers }}\n\nPlease provide:\n\n1. **TOP 10 RESTAURANTS** - Include a mix of local cuisine, fine dining, and casual options. For each restaurant provide:\n   - Name\n   - Type of cuisine\n   - Price range ($, $$, $$$, $$$$)\n   - Brief description\n   - Must-try dish\n\n2. **TOP 10 MONUMENTS & HISTORICAL SITES** - Include famous landmarks and hidden gems. For each provide:\n   - Name\n   - Brief history/description\n   - Best time to visit\n   - Estimated visit duration\n   - Entry fee (if any)\n\n3. **TOP 10 PLACES TO VISIT** - Include parks, neighborhoods, markets, viewpoints, etc. For each provide:\n   - Name\n   - Description\n   - Best time to visit\n   - Tips for visitors\n\n4. **LOCAL TIPS** - Provide:\n   - Best transportation options\n   - Local customs to be aware of\n   - Safety tips\n   - Best neighborhoods to stay in\n   - Common scams to avoid\n\n5. **SUGGESTED ITINERARY** - Create a day-by-day suggested itinerary based on the travel dates.\n\nFormat your response in a clear, organized manner with proper sections and bullet points.",
        "promptType": "define"
      },
      "typeVersion": 1.4
    },
    {
      "id": "bca8f877-e48e-4862-877e-924b0ed70ac2",
      "name": "OpenAI Recommendations Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -224,
        896
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "5f433cd4-00c3-454b-a25f-09702f6cc23f",
      "name": "Merge All Data",
      "type": "n8n-nodes-base.merge",
      "position": [
        240,
        496
      ],
      "parameters": {
        "numberInputs": 3
      },
      "typeVersion": 3
    },
    {
      "id": "08cd8ec8-c779-49b1-a179-6d0738aa1b6e",
      "name": "Process All Data",
      "type": "n8n-nodes-base.code",
      "position": [
        464,
        496
      ],
      "parameters": {
        "jsCode": "// Get all input items from all inputs\nconst allItems = [];\n\n// Input 0: Hotels from Booking.com HTTP Request\nconst hotelsInput = $input.all().filter((item, index) => {\n  return $('HTTP Request Hotels').all().some(h => h.json === item.json);\n});\n\n// Input 1: Flights from Google Flights HTTP Request  \nconst flightsInput = $input.all().filter((item, index) => {\n  return $('HTTP Request Flights').all().some(f => f.json === item.json);\n});\n\n// Input 2: AI Recommendations\nconst aiInput = $input.all().filter((item, index) => {\n  return $('AI Recommendations').all().some(a => a.json === item.json);\n});\n\n// Get trip details from Set Variables\nconst tripDetails = $('Set Variables').first().json;\n\n// Process Hotels - directly from array\nlet hotels = [];\nfor (const item of $('HTTP Request Hotels').all()) {\n  const data = item.json;\n  \n  // If it's an array of hotels directly\n  if (Array.isArray(data)) {\n    hotels = data.slice(0, 5).map(h => ({\n      name: h.name || 'Unknown Hotel',\n      rating: h.rating || 'N/A',\n      price: h.price ? `$${Math.round(h.price)}` : 'N/A',\n      location: h.address?.full || h.location || 'N/A',\n      url: h.url || null,\n      stars: h.stars || null,\n      reviews: h.reviews || null\n    }));\n  }\n  // If it's a single hotel object with these fields\n  else if (data.name && data.url) {\n    hotels.push({\n      name: data.name,\n      rating: data.rating || 'N/A',\n      price: data.price ? `$${Math.round(data.price)}` : 'N/A',\n      location: data.address?.full || 'N/A',\n      url: data.url,\n      stars: data.stars || null,\n      reviews: data.reviews || null\n    });\n  }\n}\n\n// Limit to top 5 hotels, sorted by rating\nif (hotels.length > 5) {\n  hotels = hotels.sort((a, b) => (b.rating || 0) - (a.rating || 0)).slice(0, 5);\n}\n\n// Process Flights\nlet flights = [];\nfor (const item of $('HTTP Request Flights').all()) {\n  const data = item.json;\n  \n  // Handle array response\n  if (Array.isArray(data)) {\n    for (const flightData of data) {\n      if (flightData.best_flights) {\n        flights = flights.concat(flightData.best_flights.map(f => ({\n          airline: f.flights?.[0]?.airline || 'Unknown',\n          flightNumber: f.flights?.[0]?.flight_number || '',\n          price: flightData.price_insights?.lowest_price || f.price || 'N/A',\n          duration: f.total_duration || 'N/A',\n          stops: (f.flights?.length || 1) - 1,\n          departure: f.flights?.[0]?.departure_airport?.time || 'N/A',\n          arrival: f.flights?.[f.flights?.length - 1]?.arrival_airport?.time || 'N/A',\n          departureAirport: f.flights?.[0]?.departure_airport?.id || '',\n          arrivalAirport: f.flights?.[f.flights?.length - 1]?.arrival_airport?.id || '',\n          airplane: f.flights?.[0]?.airplane || '',\n          // No direct booking URL from Google Flights API\n        })));\n      }\n      if (flightData.other_flights) {\n        flights = flights.concat(flightData.other_flights.slice(0, 2).map(f => ({\n          airline: f.flights?.[0]?.airline || 'Unknown',\n          flightNumber: f.flights?.[0]?.flight_number || '',\n          price: f.price || 'N/A',\n          duration: f.total_duration || 'N/A',\n          stops: (f.flights?.length || 1) - 1,\n          departure: f.flights?.[0]?.departure_airport?.time || 'N/A',\n          arrival: f.flights?.[f.flights?.length - 1]?.arrival_airport?.time || 'N/A',\n          departureAirport: f.flights?.[0]?.departure_airport?.id || '',\n          arrivalAirport: f.flights?.[f.flights?.length - 1]?.arrival_airport?.id || '',\n          airplane: f.flights?.[0]?.airplane || '',\n        })));\n      }\n    }\n  }\n  // Handle single object response\n  else if (data.best_flights) {\n    flights = flights.concat(data.best_flights.map(f => ({\n      airline: f.flights?.[0]?.airline || 'Unknown',\n      flightNumber: f.flights?.[0]?.flight_number || '',\n      price: data.price_insights?.lowest_price || f.price || 'N/A',\n      duration: f.total_duration || 'N/A',\n      stops: (f.flights?.length || 1) - 1,\n      departure: f.flights?.[0]?.departure_airport?.time || 'N/A',\n      arrival: f.flights?.[f.flights?.length - 1]?.arrival_airport?.time || 'N/A',\n      departureAirport: f.flights?.[0]?.departure_airport?.id || '',\n      arrivalAirport: f.flights?.[f.flights?.length - 1]?.arrival_airport?.id || '',\n      airplane: f.flights?.[0]?.airplane || '',\n    })));\n  }\n}\n\n// Limit flights to top 5\nflights = flights.slice(0, 5);\n\n// Process AI Recommendations\nlet recommendations = '';\nfor (const item of $('AI Recommendations').all()) {\n  if (item.json.text) {\n    recommendations = item.json.text;\n  } else if (item.json.output) {\n    recommendations = item.json.output;\n  }\n}\n\nreturn [{\n  json: {\n    tripDetails: {\n      destination: tripDetails.destination,\n      departureAirport: tripDetails.departureAirport,\n      destinationAirport: tripDetails.destinationAirport,\n      checkIn: tripDetails.checkinDate,\n      checkOut: tripDetails.checkoutDate,\n      travelers: tripDetails.travelers\n    },\n    hotels: hotels,\n    flights: flights,\n    recommendations: recommendations,\n    generatedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d7efd999-bab6-41c9-844d-dcfc2a5822b3",
      "name": "Create Document",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        816,
        496
      ],
      "parameters": {
        "title": "=Travel Plan - {{ $json.tripDetails.departureAirport }} to {{ $json.tripDetails.destinationAirport }} for {{ $json.tripDetails.travelers }} people",
        "folderId": "YOUR_FOLDER_ID"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2fc9cb4d-a0bb-4fe3-b964-fdce703d1ce0",
      "name": "Prepare Document Content",
      "type": "n8n-nodes-base.code",
      "position": [
        1040,
        496
      ],
      "parameters": {
        "jsCode": "const docInfo = $('Create Document').first().json;\nconst travelPlan = $('Process All Data').first().json;\nconst documentId = docInfo.id || docInfo.documentId || docInfo.docId;\n\nlet content = `\u2708\ufe0f TRAVEL PLAN: ${travelPlan.tripDetails.destination}\\n`;\ncontent += `Generated: ${travelPlan.generatedAt}\\n\\n`;\ncontent += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\ncontent += `\ud83d\udccb TRIP DETAILS\\n`;\ncontent += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\ncontent += `From: ${travelPlan.tripDetails.departureAirport}\\n`;\ncontent += `To: ${travelPlan.tripDetails.destinationAirport}\\n`;\ncontent += `Check-in: ${travelPlan.tripDetails.checkIn}\\n`;\ncontent += `Check-out: ${travelPlan.tripDetails.checkOut}\\n`;\ncontent += `Travelers: ${travelPlan.tripDetails.travelers}\\n\\n`;\n\ncontent += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\ncontent += `\u2708\ufe0f FLIGHTS\\n`;\ncontent += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nif (travelPlan.flights && travelPlan.flights.length > 0) {\n  travelPlan.flights.forEach((f, i) => {\n    content += `${i+1}. ${f.airline} ${f.flightNumber || ''}\\n`;\n    content += `   \ud83d\udcb0 Price: $${f.price}\\n`;\n    content += `   \u23f1\ufe0f Duration: ${f.duration} min | ${f.stops} stop(s)\\n`;\n    content += `   \ud83d\udeeb ${f.departure} \u2192 \ud83d\udeec ${f.arrival}\\n`;\n    if (f.airplane) content += `   \u2708\ufe0f Aircraft: ${f.airplane}\\n`;\n    content += `\\n`;\n  });\n  content += `\ud83d\udd17 Search flights: https://www.google.com/travel/flights?q=flights%20from%20${travelPlan.tripDetails.departureAirport}%20to%20${travelPlan.tripDetails.destinationAirport}\\n`;\n} else {\n  content += `No flight data available\\n`;\n}\n\ncontent += `\\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\ncontent += `\ud83c\udfe8 TOP RECOMMENDED HOTELS\\n`;\ncontent += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nif (travelPlan.hotels && travelPlan.hotels.length > 0) {\n  travelPlan.hotels.forEach((h, i) => {\n    content += `\\n${i+1}. ${h.name}\\n`;\n    content += `   \u2b50 Rating: ${h.rating}/10`;\n    if (h.stars) content += ` (${h.stars}-star)`;\n    if (h.reviews) content += ` - ${h.reviews} reviews`;\n    content += `\\n`;\n    content += `   \ud83d\udcb0 Price: ${h.price}\\n`;\n    content += `   \ud83d\udccd Location: ${h.location}\\n`;\n    if (h.url) content += `   \ud83d\udd17 Book here: ${h.url}\\n`;\n  });\n} else {\n  content += `No hotel data available\\n`;\n}\n\ncontent += `\\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\ncontent += `\ud83c\udfaf AI RECOMMENDATIONS\\n`;\ncontent += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\ncontent += travelPlan.recommendations || 'No recommendations available';\n\nreturn [{\n  json: {\n    documentId: documentId,\n    documentUrl: `https://docs.google.com/document/d/${documentId}/edit`,\n    content: content\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "cc3d3c09-77a9-4b46-9404-48a172e3d84a",
      "name": "Update Document",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        1248,
        496
      ],
      "parameters": {
        "actionsUi": {
          "actionFields": [
            {
              "text": "={{ $json.content }}",
              "action": "insert"
            }
          ]
        },
        "operation": "update",
        "documentURL": "={{ $json.documentId }}"
      },
      "credentials": {
        "googleDocsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2
    },
    {
      "id": "899c6918-0e89-407c-9c41-93113a5cbb74",
      "name": "Prepare Form Ending",
      "type": "n8n-nodes-base.code",
      "position": [
        1472,
        496
      ],
      "parameters": {
        "jsCode": "// Get the document URL from Prepare Document Content node\nconst preparedData = $('Prepare Document Content').first().json;\n\nreturn [{\n  json: {\n    documentUrl: preparedData.documentUrl\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "cafd3d14-46ac-4845-9ba6-4ff2656a71f0",
      "name": "Form Ending",
      "type": "n8n-nodes-base.form",
      "position": [
        1696,
        496
      ],
      "parameters": {
        "options": {},
        "operation": "completion",
        "completionTitle": "\u2705 Your travel plan is ready!",
        "completionMessage": "=\ud83d\udcc4 View your document here:\n\n{{ $json.documentUrl }}\n\nYour personalized travel plan includes:\n\u2022 Top 3 recommended hotels\n\u2022 Available flights\n\u2022 Restaurant recommendations\n\u2022 Must-see monuments & attractions\n\u2022 Local tips & suggested itinerary\n\nEnjoy your trip! \u2708\ufe0f\ud83c\udf0d"
      },
      "typeVersion": 2.4
    },
    {
      "id": "84c80c0b-f354-43a1-a0a3-527f7680e916",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1408,
        16
      ],
      "parameters": {
        "width": 460,
        "height": 920,
        "content": "## Try It Out!\n### Collect travel details via a form, then scrape flights and hotels while AI generates recommendations \u2014 all compiled into a Google Doc.\n\nGreat for travel agencies, personal trip planning, or any service that needs to deliver a complete travel brief automatically.\n\n### How it works\n* User submits a travel form with airports, dates, and number of travelers.\n* Three parallel tasks run: Booking.com hotel scraping, Google Flights scraping, and OpenAI recommendation generation.\n* Results are merged, formatted, and written to a new Google Doc.\n* The user receives a link to their personalized travel plan.\n\n### Setup steps\n* Create an [Apify](https://apify.com) account and add your API token as both an \"Apify API\" credential and an \"HTTP Query Auth\" credential (parameter name: `token`).\n* Add your OpenAI API key as an \"OpenAI\" credential.\n* Connect Google Docs via OAuth2 and update the `folderId` in the \"Create Document\" node.\n* Activate the workflow and share the form URL.\n\n### Requirements\n* Apify account (for Booking.com and Google Flights scrapers)\n* OpenAI API key\n* Google account with Docs and Drive access\n\n### Need Help?\nJoin the [Discord](https://discord.com/invite/XPKeKXeB7d) or ask in the [Forum](https://community.n8n.io/)!\n\nHappy Hacking!"
      },
      "typeVersion": 1
    },
    {
      "id": "edb7e1c3-61b9-482d-b953-00328f402eec",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -928,
        192
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "height": 420,
        "content": "## 1. Trigger and input\nThe form collects departure/destination airports, dates, and traveler count. The Set node normalizes these into variables used by all downstream nodes."
      },
      "typeVersion": 1
    },
    {
      "id": "389ecca6-1893-4e00-b38f-8314f1b62af2",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -432,
        32
      ],
      "parameters": {
        "color": 7,
        "width": 500,
        "height": 660,
        "content": "## 2. Parallel data fetching\n[Apify integration docs](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-apify/)\n\nThree tasks run in parallel: Booking.com scrapes top 5 hotels by review score, Google Flights scrapes the best flights, and OpenAI generates restaurant, attraction, and itinerary recommendations."
      },
      "typeVersion": 1
    },
    {
      "id": "3c7f0f3a-10de-4e6c-bd88-f89aebfb05a1",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        208,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 420,
        "content": "## 3. Merge and process\nAll three data streams are merged into a single item. The Code node structures hotels, flights, and AI recommendations into a clean object for the Google Doc."
      },
      "typeVersion": 1
    },
    {
      "id": "d13a3f3e-fa66-451f-a733-6bde3e4789b6",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        784,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 1120,
        "height": 420,
        "content": "## 4. Generate Google Doc and respond\n[Google Docs node docs](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googledocs/)\n\nA new Google Doc is created, populated with the formatted travel plan, and the form completion page returns the document link to the user."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "e0c24208-6d3e-4cfe-96a2-b008d62805fe",
  "connections": {
    "Travel Form": {
      "main": [
        [
          {
            "node": "Set Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Variables": {
      "main": [
        [
          {
            "node": "Booking.com Scraper",
            "type": "main",
            "index": 0
          },
          {
            "node": "Google Flights Scraper",
            "type": "main",
            "index": 0
          },
          {
            "node": "AI Recommendations",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge All Data": {
      "main": [
        [
          {
            "node": "Process All Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Document": {
      "main": [
        [
          {
            "node": "Prepare Document Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Document": {
      "main": [
        [
          {
            "node": "Prepare Form Ending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process All Data": {
      "main": [
        [
          {
            "node": "Create Document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Recommendations": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Booking.com Scraper": {
      "main": [
        [
          {
            "node": "HTTP Request Hotels",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request Hotels": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Form Ending": {
      "main": [
        [
          {
            "node": "Form Ending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request Flights": {
      "main": [
        [
          {
            "node": "Merge All Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Google Flights Scraper": {
      "main": [
        [
          {
            "node": "HTTP Request Flights",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Document Content": {
      "main": [
        [
          {
            "node": "Update Document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Recommendations Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Recommendations",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    }
  }
}