{
  "id": "6vj6GA2bdFbUXGlc",
  "name": "Find cheap flight deals using AI, Telegram and BrowserAct",
  "tags": [],
  "nodes": [
    {
      "id": "ae57867e-ffbe-4188-9955-00725cc17f51",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -720,
        496
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "{\n  \"type\": \"travel\",\n  \"location\": \"San Francisco\"\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "f295c258-781c-46c1-8338-639dcc26b08c",
      "name": "Validate user Input",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -848,
        272
      ],
      "parameters": {
        "text": "={{ $json.message.text }}",
        "options": {
          "systemMessage": "You are an intelligent Intent Classifier and Entity Extractor for a travel bot. Your sole purpose is to analyze the user's input text and categorize it into one of three specific types based on their intent and the availability of data.\n\nTask: Analyze the user's input string and return a single JSON object based on the following logic:\n\n1. Travel Request with Location:\n\nTrigger: The user is explicitly asking for flight prices, travel offers, deals, or tickets AND you can identify a specific Departure City (e.g., \"flights from London\", \"deals leaving NYC\").\n\nAction: Extract the departure city.\n\nOutput: Set \"type\": \"travel\" and \"location\": \"City Name\".\n\n2. Travel Request without Location:\n\nTrigger: The user is asking for travel deals or prices (e.g., \"I want cheap flights\", \"Show me offers\") but DID NOT specify a departure city.\n\nAction: Flag as missing data.\n\nOutput: Set \"type\": \"nodata\" and \"location\": null.\n\n3. General Chat:\n\nTrigger: The user is engaging in casual conversation (e.g., \"Hello\", \"How are you?\", \"Who are you?\") that is unrelated to a specific travel search.\n\nAction: Classify as chat.\n\nOutput: Set \"type\": \"chat\" and \"location\": null.\n\nOutput Format: You must respond only with a raw JSON object. Do not add markdown formatting or conversational text.\n\n{\n  \"type\": \"travel\" | \"nodata\" | \"chat\",\n  \"location\": \"City Name\" | null\n}\nExamples of Behavior:\nInput: \"Find me cheap flights from Berlin to Paris\" Output:\n\n{\n  \"type\": \"travel\",\n  \"location\": \"Berlin\"\n}\nInput: \"I want to go on a vacation show me prices\" Output:\n\n{\n  \"type\": \"nodata\",\n  \"location\": null\n}\nInput: \"Good morning, how does this bot work?\" Output:\n\n{\n  \"type\": \"chat\",\n  \"location\": null\n}\nInput: \"Deals departing form San Francisco\" Output:\n\n{\n  \"type\": \"travel\",\n  \"location\": \"San Francisco\"\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "8c74bb74-24e8-450a-9875-a4682abcc710",
      "name": "Validate user Inputs",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -848,
        496
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c184ff93-0aed-445e-8ec4-1a66a91d542b",
      "name": "Google Gemini",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -128,
        736
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0dd6095d-a693-46ba-8e77-a83476b97b34",
      "name": "Structured Output",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        624,
        512
      ],
      "parameters": {
        "autoFix": true,
        "jsonSchemaExample": "\n{\n  \"Telegram\": [\n    \"String 1 (HTML formatted, max 3500 chars)\",\n    \"String 2 (If needed due to length)\"\n  ]\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "527af11f-88b2-4a44-abe0-4bad44ced719",
      "name": "OpenRouter Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        512,
        512
      ],
      "parameters": {
        "model": "openai/gpt-4.1",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "995ebd6f-dc22-4b8b-a585-1b4c803b3224",
      "name": "Loop Over Items",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        976,
        304
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "33b7eab4-658a-4e92-a22f-e49dc66fe069",
      "name": "Chatting With User",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -128,
        576
      ],
      "parameters": {
        "text": "=Input type : {{ $json.output.Type }} | User Input : {{ $('User Sends Message to Bot').item.json.message.text }}",
        "options": {
          "systemMessage": "if the input type is nodata ask the user to provide the Departure location.\n\nif the input type is chat, analyze the user input and generate single response\n\nsend the result out as a raw text.\navoid using any tags or notations like ```text ```"
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "71544b37-f48b-4c7a-babc-d3ce7ad62e5a",
      "name": "Answer the User",
      "type": "n8n-nodes-base.telegram",
      "position": [
        176,
        576
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "chatId": "={{ $('User Sends Message to Bot').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f04821b9-be4a-4de0-bc7c-c1b23a339ed1",
      "name": "Check For Input Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        -496,
        256
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "9e003f66-d011-459a-a313-b603c0e14551",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.output.type }}",
                    "rightValue": "travel"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "2ebb24ec-bd32-4e96-a022-93d066fda17f",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.output.type }}",
                    "rightValue": "chat"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "24d355dd-efec-4946-96fe-3d82f3ee4d71",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.output.type }}",
                    "rightValue": "nodata"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "b4d78d2d-bc32-4dd0-8465-67cef4ae2c45",
      "name": "User Sends Message to Bot",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -1024,
        272
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9f86f90d-26a1-4602-befe-417292fe1fb7",
      "name": "Process Initialization Alert",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -48,
        144
      ],
      "parameters": {
        "text": "=Ok give me few minutes.",
        "chatId": "={{ $('User Sends Message to Bot').first().json.message.chat.id }}",
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ea9f17c3-584d-4b26-ab22-799d95a65133",
      "name": "Run \"Low-Const Travel Finder\" workflow",
      "type": "n8n-nodes-browseract.browserAct",
      "position": [
        -48,
        304
      ],
      "parameters": {
        "type": "WORKFLOW",
        "timeout": 7200,
        "workflowId": "72181632540107285",
        "workflowConfig": {
          "value": {
            "input-Location": "={{ $json.output.location }}"
          },
          "schema": [
            {
              "id": "input-Momondo",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "description": "If left blank, the default value defined in BrowserAct will be used.",
              "displayName": "Momondo",
              "defaultMatch": true
            },
            {
              "id": "input-Location",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "description": "If left blank, the default value defined in BrowserAct will be used.",
              "displayName": "Location",
              "defaultMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "input-Location"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "open_incognito_mode": false
      },
      "credentials": {
        "browserActApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "32fd8b4f-fded-4992-9e7d-560018c4524d",
      "name": "Analyze Data & Generate Response",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        512,
        304
      ],
      "parameters": {
        "text": "=Travels Data :{{ $json.output.string }},\nDeparture Location : {{ $('Validate user Input').first().json.output.location }}",
        "options": {
          "systemMessage": "Role: You are an expert Travel Deal Curator for a Telegram channel. Your job is to take raw flight data and a departure location, then transform it into a highly attractive, scannable, and HTML-formatted Telegram post.\n\nInput Data:\n\ndeparture_city: The city where flights originate.\n\nflight_data: A JSON array containing objects with location, date_range, stops, and price.\n\nOutput Specification: You must return a valid JSON object with the following structure: (do not add any markdow like \"```json ```\")\n\n\n{\n  \"Telegram\": [\n    \"String 1 (HTML formatted, max 3500 chars)\",\n    \"String 2 (If needed due to length)\"\n  ]\n}\n\nFormatting Guidelines:\n\nHeader: Start with an eye-catching title using the Departure City. (e.g., \u2708\ufe0f **Cheap Flights from [City]** \ud83c\udf0d)\n\nStructure: Group flights logically. If the list is long (over 10 items), group them by Price Range (e.g., \"\ud83d\udd25 Steal Deals under 45\" vs \"\ud83d\udc8e Great Value\") or by Month to make it scannable.\n\nLine Items: Make each flight easy to read. Use specific HTML tags: <b>, <i>, <code>, or <a href> (if links provided).\n\nStyle: Flag Destination | Date | Price\n\nExample: \ud83c\uddee\ud83c\uddf9 Milan \u2014 <i>Jan 23-25</i> \u2014 <b>40\u20ac</b>\n\nFlags: Infer the country flag based on the city name (e.g., Oslo = \ud83c\uddf3\ud83c\uddf4, Milan = \ud83c\uddee\ud83c\uddf9).\n\nStops: If stops is \"Nonstop\", use a symbol like \u26a1\ufe0f or omit it to save space. If there are stops, explicitly state it (e.g., \"1 Stop\").\n\nCharacter Limit: Telegram has a message limit. Monitor the character count.\n\nIf the text exceeds 3500 characters, split the message at a logical section break (e.g., between groups) into the next string in the JSON array.\n\nFooter: Add a brief \"Book Now\" or \"Prices subject to change\" footer.\n\nTask: Processing Logic\nSort the list by price (low to high).\n\nGroup the data if necessary to improve readability.\n\nFormat using emojis and permitted HTML (<b>, <i>, <u>, <s>, <code>, <pre>).\n\nValidate length. Split into multiple array elements if > 3500 chars.\n\nOutput the final JSON."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 3
    },
    {
      "id": "1121479f-7036-42e7-9d25-c09a4d654888",
      "name": "Split Out Generated Data",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        784,
        304
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "output.Telegram"
      },
      "typeVersion": 1
    },
    {
      "id": "9b67af05-9df4-492b-bce2-ff78db51285c",
      "name": "Avoid Rate Limits",
      "type": "n8n-nodes-base.wait",
      "position": [
        1184,
        272
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "e930941e-30b9-44c8-8f72-b7537eeb39c7",
      "name": "Send Travel List to User",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1408,
        304
      ],
      "parameters": {
        "text": "={{ $('Loop Over Items').item.json[\"output.Telegram\"] }}",
        "chatId": "={{ $('User Sends Message to Bot').first().json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "4e6d66e5-1774-4c8d-a540-4de1f46a722c",
      "name": "Step 1 Explanation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1024,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 636,
        "height": 124,
        "content": "### \ud83d\udd75\ufe0f Step 1: Intent Analysis\n\nThe workflow begins by validating user input via Telegram. It identifies if the user is asking for travel deals, checks for a specified departure location, or is simply engaging in general conversation."
      },
      "typeVersion": 1
    },
    {
      "id": "1df5d09d-e4df-4b26-bcc8-68396cb9ba60",
      "name": "Step 2 Explanation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -224,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 572,
        "height": 124,
        "content": "### \u2708\ufe0f Step 2: Automated Deal Scrape\n\nOnce a location is validated, BrowserAct automates a browser session on travel sites like Momondo to find the lowest prices. The system immediately notifies the user that the search is underway."
      },
      "typeVersion": 1
    },
    {
      "id": "ac2ca358-88c2-4cf4-8b0a-8fc2bf044565",
      "name": "Step 3 Explanation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        512,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 1036,
        "height": 108,
        "content": "### \ud83d\udcca Step 3: AI Curation & Delivery\n\nAn AI agent processes the raw flight data, sorts it by price, and generates a scannable HTML report. Large lists are automatically split to respect Telegram limits and delivered sequentially to the user."
      },
      "typeVersion": 1
    },
    {
      "id": "36e9af59-8ec3-44b1-b60b-dc453e0de9c5",
      "name": "Step 4 Explanation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -192,
        864
      ],
      "parameters": {
        "color": 7,
        "width": 580,
        "height": 120,
        "content": "### \ud83d\udcac Step 2-2: Conversational Fallback\n\nIf no link is present in the user's message, this branch engages the user in natural conversation or prompts them to provide a URL for processing."
      },
      "typeVersion": 1
    },
    {
      "id": "b0451bb6-bd86-4b93-b558-ddf256df1297",
      "name": "Documentation",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1440,
        -304
      ],
      "parameters": {
        "width": 380,
        "height": 520,
        "content": "## \u26a1 Workflow Overview & Setup\n\n**Summary:** This automation extracts the departure city from Telegram messages and uses BrowserAct to scrape travel sites for the cheapest flight deals, delivering a formatted list back to the user.\n\n### Requirements\n* **Credentials:** Telegram, BrowserAct, OpenRouter (GPT-4), Google Gemini (PaLM).\n* **Mandatory:** BrowserAct API (Template: **Low-Cost Travel Finder**)\n\n### How to Use\n1.  **Credentials:** Set up your Telegram Bot, BrowserAct, and AI model credentials in n8n.\n2.  **BrowserAct Template:** Ensure you have the **Low-Cost Travel Finder** template saved in your BrowserAct account.\n3.  **Search:** Send a message like \"Low-Cost Travel Finder For London\" to your Telegram bot to receive real-time deal alerts.\n\n### Need Help?\n[How to Find Your BrowserAct API Key & Workflow ID](https://docs.browseract.com)\n[How to Connect n8n to BrowserAct](https://docs.browseract.com)\n[How to Use & Customize BrowserAct Templates](https://docs.browseract.com)"
      },
      "typeVersion": 1
    },
    {
      "id": "48ec5d58-e69e-4e36-8d4e-42404e61381a",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1024,
        -304
      ],
      "parameters": {
        "color": 6,
        "width": 624,
        "height": 368,
        "content": "@[youtube](W4EYza-Te5w)"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1897423c-cd71-4021-89ba-1a42589f4e08",
  "connections": {
    "Google Gemini": {
      "ai_languageModel": [
        [
          {
            "node": "Chatting With User",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [],
        [
          {
            "node": "Avoid Rate Limits",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Model": {
      "ai_languageModel": [
        [
          {
            "node": "Analyze Data & Generate Response",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Avoid Rate Limits": {
      "main": [
        [
          {
            "node": "Send Travel List to User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output": {
      "ai_outputParser": [
        [
          {
            "node": "Analyze Data & Generate Response",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Chatting With User": {
      "main": [
        [
          {
            "node": "Answer the User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate user Input": {
      "main": [
        [
          {
            "node": "Check For Input Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check For Input Type": {
      "main": [
        [
          {
            "node": "Process Initialization Alert",
            "type": "main",
            "index": 0
          },
          {
            "node": "Run \"Low-Const Travel Finder\" workflow",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Chatting With User",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Chatting With User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate user Inputs": {
      "ai_languageModel": [
        [
          {
            "node": "Validate user Input",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Send Travel List to User": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out Generated Data": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Validate user Input",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "User Sends Message to Bot": {
      "main": [
        [
          {
            "node": "Validate user Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Data & Generate Response": {
      "main": [
        [
          {
            "node": "Split Out Generated Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run \"Low-Const Travel Finder\" workflow": {
      "main": [
        [
          {
            "node": "Analyze Data & Generate Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}