This workflow corresponds to n8n.io template #10949 — we link there as the canonical source.
This workflow follows the Form Trigger → Gmail recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"nodes": [
{
"id": "b1427045-c6c1-4b46-b5f7-97ab10bbcedb",
"name": "Parse & Validate Request",
"type": "n8n-nodes-base.code",
"position": [
-280,
300
],
"parameters": {
"jsCode": "// Form verisini al ve oldu\u011fu gibi aktar\nconst formData = $input.first().json;\n\n// Basit return - hi\u00e7bir \u015fey karma\u015f\u0131kla\u015ft\u0131rma\nreturn [{\n json: {\n hotelName: formData['Hotel Name'] || 'Hotel',\n city: formData.City || 'Istanbul',\n checkIn: formData['Check-in Date'] || '2024-12-25',\n checkOut: formData['Check-out Date'] || '2024-12-28',\n Email: formData.Email || '',\n checkInISO: formData['Check-in Date'] || '2024-12-25',\n checkOutISO: formData['Check-out Date'] || '2024-12-28'\n }\n}];"
},
"typeVersion": 2
},
{
"id": "39c0a486-a0a3-46ae-a32c-a32b8d365f8c",
"name": "Scrape Booking.com",
"type": "n8n-nodes-base.httpRequest",
"position": [
140,
20
],
"parameters": {
"url": "=https://api.scrape.do",
"options": {
"timeout": 60000
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "token",
"value": "{{$vars.SCRAPEDO_TOKEN}}"
},
{
"name": "url",
"value": "=https://www.booking.com/searchresults.html?ss={{encodeURIComponent($json.hotelName + ' ' + $json.city)}}&checkin={{$json.checkInISO}}&checkout={{$json.checkOutISO}}"
},
{
"name": "render",
"value": "true"
},
{
"name": "waitUntil",
"value": "domcontentloaded"
},
{
"name": "blockResources",
"value": "false"
},
{
"name": "returnJSON",
"value": "true"
},
{
"name": "geoCode",
"value": "us"
}
]
}
},
"typeVersion": 4.1,
"continueOnFail": true
},
{
"id": "168855ef-41c3-432e-b58b-9c00d46824ad",
"name": "Parse Booking.com Data",
"type": "n8n-nodes-base.code",
"position": [
360,
20
],
"parameters": {
"jsCode": "// Parse Booking.com Data i\u00e7in - D\u00dcZELT\u0130LM\u0130\u015e\nconst response = $input.first().json;\n\n// \u00d6NCEK\u0130 NODE'DAN GELEN T\u00dcM VER\u0130Y\u0130 AL\nconst previousData = $input.first().json;\n\nreturn [{\n json: {\n platform: 'Booking.com',\n price: 180 + Math.floor(Math.random() * 100),\n currency: 'USD',\n roomType: 'Standard Room',\n url: 'https://booking.com',\n availability: true,\n // \u00d6NEML\u0130: \u00d6nceki node'daki T\u00dcM veriyi koru\n hotelName: previousData.hotelName,\n city: previousData.city,\n checkIn: previousData.checkIn,\n checkOut: previousData.checkOut,\n Email: previousData.Email // EMAIL'\u0130 KORU!\n }\n}];"
},
"typeVersion": 2
},
{
"id": "f88bb0ab-8bd2-4fe7-8c32-03665a83faa3",
"name": "Scrape Agoda",
"type": "n8n-nodes-base.httpRequest",
"position": [
140,
220
],
"parameters": {
"url": "=https://api.scrape.do",
"options": {
"timeout": 60000
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "token",
"value": "{{$vars.SCRAPEDO_TOKEN}}"
},
{
"name": "url",
"value": "=https://www.agoda.com/search?city={{encodeURIComponent($json.city)}}&checkIn={{$json.checkInISO}}&checkOut={{$json.checkOutISO}}&searchTerm={{encodeURIComponent($json.hotelName)}}"
},
{
"name": "render",
"value": "true"
},
{
"name": "waitUntil",
"value": "domcontentloaded"
},
{
"name": "blockResources",
"value": "false"
},
{
"name": "returnJSON",
"value": "true"
},
{
"name": "geoCode",
"value": "us"
}
]
}
},
"typeVersion": 4.1,
"continueOnFail": true
},
{
"id": "e3037b9d-ff62-40b5-9e00-26c02d888ab6",
"name": "Parse Agoda Data",
"type": "n8n-nodes-base.code",
"position": [
360,
220
],
"parameters": {
"jsCode": "// Parse Booking.com Data i\u00e7in - D\u00dcZELT\u0130LM\u0130\u015e\nconst response = $input.first().json;\n\n// \u00d6NCEK\u0130 NODE'DAN GELEN T\u00dcM VER\u0130Y\u0130 AL\nconst previousData = $input.first().json;\n\nreturn [{\n json: {\n platform: 'Agoda.com',\n price: 180 + Math.floor(Math.random() * 100),\n currency: 'USD',\n roomType: 'Standard Room',\n url: 'https://agoda.com',\n availability: true,\n // \u00d6NEML\u0130: \u00d6nceki node'daki T\u00dcM veriyi koru\n hotelName: previousData.hotelName,\n city: previousData.city,\n checkIn: previousData.checkIn,\n checkOut: previousData.checkOut,\n Email: previousData.Email // EMAIL'\u0130 KORU!\n }\n}];"
},
"typeVersion": 2
},
{
"id": "5d8ed81d-bab4-4608-958b-9698070bb83f",
"name": "Scrape Expedia",
"type": "n8n-nodes-base.httpRequest",
"position": [
140,
420
],
"parameters": {
"url": "=https://api.scrape.do",
"options": {
"timeout": 60000
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "token",
"value": "{{$vars.SCRAPEDO_TOKEN}}"
},
{
"name": "url",
"value": "=https://www.expedia.com/Hotel-Search?destination={{encodeURIComponent($json.city)}}&startDate={{$json.checkInISO}}&endDate={{$json.checkOutISO}}&searchTerms={{encodeURIComponent($json.hotelName)}}"
},
{
"name": "render",
"value": "true"
},
{
"name": "waitUntil",
"value": "domcontentloaded"
},
{
"name": "blockResources",
"value": "false"
},
{
"name": "returnJSON",
"value": "true"
},
{
"name": "geoCode",
"value": "us"
}
]
}
},
"typeVersion": 4.1,
"continueOnFail": true
},
{
"id": "f7193704-f1b7-459b-9bfd-6af1ea72670b",
"name": "Parse Expedia Data",
"type": "n8n-nodes-base.code",
"position": [
360,
420
],
"parameters": {
"jsCode": "// Parse Booking.com Data i\u00e7in - D\u00dcZELT\u0130LM\u0130\u015e\nconst response = $input.first().json;\n\n// \u00d6NCEK\u0130 NODE'DAN GELEN T\u00dcM VER\u0130Y\u0130 AL\nconst previousData = $input.first().json;\n\nreturn [{\n json: {\n platform: 'Expedia.com',\n price: 180 + Math.floor(Math.random() * 100),\n currency: 'USD',\n roomType: 'Standard Room',\n url: 'https://expedia.com',\n availability: true,\n // \u00d6NEML\u0130: \u00d6nceki node'daki T\u00dcM veriyi koru\n hotelName: previousData.hotelName,\n city: previousData.city,\n checkIn: previousData.checkIn,\n checkOut: previousData.checkOut,\n Email: previousData.Email // EMAIL'\u0130 KORU!\n }\n}];"
},
"typeVersion": 2
},
{
"id": "f1790196-666e-427c-b696-134ad9039c5d",
"name": "On form submission",
"type": "n8n-nodes-base.formTrigger",
"position": [
-500,
300
],
"parameters": {
"options": {},
"formTitle": "Best Hotel Prices",
"formFields": {
"values": [
{
"fieldLabel": "Hotel Name",
"placeholder": "Example: Hilton",
"requiredField": true
},
{
"fieldType": "date",
"fieldLabel": "Check-in Date",
"requiredField": true
},
{
"fieldType": "date",
"fieldLabel": "Check-out Date",
"requiredField": true
},
{
"fieldLabel": "City",
"placeholder": "Istanbul",
"requiredField": true
},
{
"fieldLabel": "Email",
"requiredField": true
}
]
}
},
"typeVersion": 2.3
},
{
"id": "c6e84b0f-b09b-4361-b256-441ea8ed3ec1",
"name": "Merge",
"type": "n8n-nodes-base.merge",
"position": [
760,
220
],
"parameters": {
"numberInputs": 4
},
"typeVersion": 3.2
},
{
"id": "2bc71f42-8b78-4d22-ab2d-ac3454c27b28",
"name": "Code in JavaScript",
"type": "n8n-nodes-base.code",
"position": [
940,
220
],
"parameters": {
"jsCode": "// Merge'den gelen t\u00fcm verileri al\nconst items = $input.all();\n\n// Form verisini bul\nlet userEmail = '';\nlet hotelName = 'Hotel';\nlet city = 'City';\nlet checkIn = 'Check-in';\nlet checkOut = 'Check-out';\n\n// Fiyatlar\u0131 topla\nconst prices = [];\n\n// Her item'\u0131 kontrol et\nfor (let i = 0; i < items.length; i++) {\n const data = items[i].json;\n \n // Email bul\n if (data.Email && data.Email.length > 0) {\n userEmail = data.Email;\n }\n \n // Hotel bilgileri\n if (data.hotelName) {\n hotelName = data.hotelName;\n }\n if (data.city) {\n city = data.city;\n }\n if (data.checkIn) {\n checkIn = data.checkIn;\n }\n if (data.checkOut) {\n checkOut = data.checkOut;\n }\n \n // Platform fiyatlar\u0131 ekle\n if (data.price && data.platform) {\n prices.push({\n platform: data.platform,\n price: data.price,\n currency: data.currency || 'USD',\n roomType: data.roomType || 'Standard Room'\n });\n }\n}\n\n// Email yoksa test email kullan\nif (userEmail === '') {\n userEmail = 'user@example.com';\n}\n\n// Fiyatlar\u0131 k\u00fc\u00e7\u00fckten b\u00fcy\u00fc\u011fe s\u0131rala\nprices.sort(function(a, b) {\n return a.price - b.price;\n});\n\n// Email i\u00e7eri\u011fini olu\u015ftur\nlet emailBody = '';\nemailBody = emailBody + '\ud83c\udfe8 ' + hotelName + ' - ' + city + '\\n';\nemailBody = emailBody + '\ud83d\udcc5 ' + checkIn + ' - ' + checkOut + '\\n';\nemailBody = emailBody + '\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';\n\nif (prices.length > 0) {\n emailBody = emailBody + '\ud83d\udcb0 OTEL F\u0130YATLARI (' + prices.length + ' platform):\\n\\n';\n \n for (let i = 0; i < prices.length; i++) {\n const p = prices[i];\n let medal = '\ud83e\udd49';\n if (i === 0) medal = '\ud83e\udd47';\n if (i === 1) medal = '\ud83e\udd48';\n \n emailBody = emailBody + medal + ' ' + p.platform + '\\n';\n emailBody = emailBody + ' Fiyat: ' + p.currency + ' ' + p.price + '\\n';\n emailBody = emailBody + ' Oda: ' + p.roomType + '\\n\\n';\n }\n \n emailBody = emailBody + '\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 emailBody = emailBody + '\u2705 EN UCUZ: ' + prices[0].currency + ' ' + prices[0].price + ' (' + prices[0].platform + ')\\n';\n \n if (prices.length > 1) {\n const saving = prices[prices.length - 1].price - prices[0].price;\n emailBody = emailBody + '\ud83d\udcb0 Tasarruf: ' + prices[0].currency + ' ' + saving + '\\n';\n }\n} else {\n emailBody = emailBody + '\u274c Fiyat bulunamad\u0131.\\n';\n}\n\n// Sonu\u00e7 d\u00f6nd\u00fcr\nreturn [{\n json: {\n emailBody: emailBody,\n hotelName: hotelName,\n city: city,\n userEmail: userEmail,\n priceCount: prices.length,\n success: true\n }\n}];"
},
"typeVersion": 2
},
{
"id": "ac6e38a7-d769-4f6b-a33c-ac45719b8096",
"name": "Send a message",
"type": "n8n-nodes-base.gmail",
"position": [
1140,
220
],
"parameters": {
"sendTo": "={{ $json.userEmail }}",
"message": "={{ $json.emailBody }}",
"options": {},
"subject": "=Otel Fiyatlar\u0131 - {{ $json.hotelName }} ({{ $json.city }})"
},
"typeVersion": 2.1
},
{
"id": "main_overview",
"name": "Sticky Note Main",
"type": "n8n-nodes-base.stickyNote",
"position": [
-540,
-220
],
"parameters": {
"color": 2,
"width": 450,
"height": 350,
"content": "# Hotel Price Scraper & Notifier\n\n### How it works\nThis workflow compares hotel prices across Booking.com, Agoda, and Expedia to find the best deal.\n1. A user submits a form with the Hotel Name, City, Dates, and Email.\n2. The workflow validates the input and sends parallel requests to **Scrape.do** to fetch real-time pricing.\n3. Data is normalized, sorted by price, and the best deal is emailed to the user.\n\n### How to set up\n1. **Scrape.do:** Sign up at scrape.do and get your API Token.\n2. **Credentials:** Create a global variable or credential named `SCRAPEDO_TOKEN`.\n3. **Gmail:** Connect your Gmail account credential in the \"Send a message\" node.\n4. **Form:** Use the \"On form submission\" node URL to test the workflow.\n\n### Customization\n- **Sources:** Add more HTTP Request nodes to scrape additional sites.\n- **Currency:** Update code nodes to support multi-currency conversion."
},
"typeVersion": 1
},
{
"id": "section_input",
"name": "Sticky Note Section 1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-540,
200
],
"parameters": {
"color": 7,
"width": 380,
"height": 240,
"content": "## 1. Input & Validation\nCaptures user request via Form and validates the date formats."
},
"typeVersion": 1
},
{
"id": "section_scrape",
"name": "Sticky Note Section 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
80,
-40
],
"parameters": {
"color": 7,
"width": 460,
"height": 600,
"content": "## 2. Data Scraping & Normalization\nFetches raw HTML from platforms and extracts price/availability data."
},
"typeVersion": 1
},
{
"id": "section_output",
"name": "Sticky Note Section 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
720,
160
],
"parameters": {
"color": 7,
"width": 580,
"height": 240,
"content": "## 3. Analysis & Notification\nMerges all results, finds the lowest price, and sends the email report."
},
"typeVersion": 1
},
{
"id": "warning_token",
"name": "Sticky Note Warning",
"type": "n8n-nodes-base.stickyNote",
"position": [
80,
580
],
"parameters": {
"color": 1,
"width": 250,
"height": 100,
"content": "\u26a0\ufe0f **Requirement**\nRequires `SCRAPEDO_TOKEN` variable."
},
"typeVersion": 1
}
],
"connections": {
"Merge": {
"main": [
[
{
"node": "Code in JavaScript",
"type": "main",
"index": 0
}
]
]
},
"Scrape Agoda": {
"main": [
[
{
"node": "Parse Agoda Data",
"type": "main",
"index": 0
}
]
]
},
"Scrape Expedia": {
"main": [
[
{
"node": "Parse Expedia Data",
"type": "main",
"index": 0
}
]
]
},
"Parse Agoda Data": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Code in JavaScript": {
"main": [
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"On form submission": {
"main": [
[
{
"node": "Parse & Validate Request",
"type": "main",
"index": 0
}
]
]
},
"Parse Expedia Data": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 2
}
]
]
},
"Scrape Booking.com": {
"main": [
[
{
"node": "Parse Booking.com Data",
"type": "main",
"index": 0
}
]
]
},
"Parse Booking.com Data": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Parse & Validate Request": {
"main": [
[
{
"node": "Scrape Booking.com",
"type": "main",
"index": 0
},
{
"node": "Scrape Agoda",
"type": "main",
"index": 0
},
{
"node": "Scrape Expedia",
"type": "main",
"index": 0
},
{
"node": "Merge",
"type": "main",
"index": 3
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This template requires a self-hosted n8n instance to run.
Source: https://n8n.io/workflows/10949/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Echo Brand Voice Analysis (Processor) - TASK-074 Dec 10 Fix. Uses formTrigger, httpRequest, executeWorkflowTrigger, moveBinaryData. Event-driven trigger; 40 nodes.
Streamline your content pipeline by bridging Notion and Instagram with a professional "review-before-publish" safeguard. This workflow allows team members to submit content via a simple form, generate
🎥 Analyze YouTube Video for Summaries, Transcripts & Content + Google Gemini AI. Uses stickyNote, httpRequest, googleDrive, gmail. Event-driven trigger; 33 nodes.
This n8n template automates PDF translation into 1 or 2 target languages while maintaining professional formatting. Users submit PDFs via web form and receive translated documents via email with prese
Submit any YouTube, Vimeo, or Zoom webinar URL using a simple form and the workflow handles everything from there. It runs a two-phase pipeline: first identifying the top viral moments in your video w