{
  "id": "651GaiLnnKcVbKfn",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Multilingual AI Hotel Guest Assistant",
  "tags": [],
  "nodes": [
    {
      "id": "72d2ef5b-b36b-496e-9ba0-cc94ce01cb65",
      "name": "Normalize Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        -688,
        96
      ],
      "parameters": {
        "jsCode": "const b = ($input.first().json.body) || {};\nreturn [{\n  json: {\n    guestId: b.guest_id || b.from || 'unknown',\n    channel: String(b.channel || 'whatsapp').toLowerCase(),\n    sender: b.from || '',\n    guestName: b.profile_name || b.name || 'Guest',\n    message: String(b.text || b.message || '').trim(),\n    receivedAt: new Date().toISOString()\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "fe68d5ee-4b24-4c95-a8f0-a70141ff57c0",
      "name": "Guest Assistant Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -400,
        96
      ],
      "parameters": {
        "text": "={{ $node['Normalize Payload'].json.message }}",
        "options": {
          "systemMessage": "You are the AI Guest Assistant for a hospitality group that runs hotels and vacation rentals.\n\nLANGUAGE RULES:\n- Detect the language of each guest message. You support Greek, English, Bulgarian and other languages.\n- Always answer in the same language the guest wrote in. Mirror their tone and keep replies short and warm.\n\nKNOWLEDGE:\n- Check in 15:00, check out 11:00. Early check in and late check out on request.\n- Breakfast 07:00 to 10:30 in the lobby restaurant.\n- Free WiFi, network HotelGuest, password is on the welcome card.\n- Pets allowed in selected rooms. Parking available on request.\n- Reception is open 24 hours.\n\nESCALATION RULES:\n- If the guest asks about refunds, booking changes, complaints, payments, medical or safety issues, or anything you cannot confirm, set escalate to true and tell the guest in their own language that you are connecting them with the front desk team.\n- Never invent prices, availability or policies.\n\nAlways return your answer through the structured output with language, reply, escalate and category."
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.7
    },
    {
      "id": "145985ce-2459-4f7b-94da-bd2a9b6272fe",
      "name": "GPT 4o mini",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -512,
        336
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {
          "temperature": 0.3
        }
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "891a330c-b38a-4fbd-81e8-3cbccd4f3efd",
      "name": "Reply Structure Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -192,
        336
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"language\": \"en\",\n  \"reply\": \"Your reply to the guest in their own language.\",\n  \"escalate\": false,\n  \"category\": \"general\"\n}"
      },
      "typeVersion": 1.2
    },
    {
      "id": "ccad907d-4185-436c-8c2e-b21ec217532a",
      "name": "Needs Human?",
      "type": "n8n-nodes-base.if",
      "position": [
        -32,
        96
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-escalate",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.output.escalate }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "ea5b0416-8970-43a0-8929-ea622a30a4f0",
      "name": "Notify Front Desk",
      "type": "n8n-nodes-base.slack",
      "position": [
        160,
        -80
      ],
      "parameters": {
        "text": "=:bellhop_bell: *Guest escalation* from {{ $node['Normalize Payload'].json.guestName }} ({{ $node['Normalize Payload'].json.channel }})\nMessage: {{ $node['Normalize Payload'].json.message }}\nCategory: {{ $node['Guest Assistant Agent'].json.output.category }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "front-desk"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "056c70a9-a10a-4998-8ba8-221773a25426",
      "name": "Route By Channel",
      "type": "n8n-nodes-base.switch",
      "position": [
        400,
        96
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $node['Normalize Payload'].json.channel }}",
                    "rightValue": "whatsapp"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 1,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $node['Normalize Payload'].json.channel }}",
                    "rightValue": "viber"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.2
    },
    {
      "id": "f622ca80-69d1-4fd4-a464-cfad9eeb3e8f",
      "name": "Log Conversation",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        928,
        80
      ],
      "parameters": {
        "columns": {
          "value": {
            "VELORA APPAREL CO.": "={{ $json.message.type }}"
          },
          "schema": [
            {
              "id": "VELORA APPAREL CO.",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "VELORA APPAREL CO.",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "VELORA APPAREL CO."
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1EV68pHdgAY4B5vTAk2djVSGEfXWhaXpSSy2GNjdCjYU/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1EV68pHdgAY4B5vTAk2djVSGEfXWhaXpSSy2GNjdCjYU",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1EV68pHdgAY4B5vTAk2djVSGEfXWhaXpSSy2GNjdCjYU/edit?usp=drivesdk",
          "cachedResultName": "Cashflow"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "beb9f2c6-6f51-47d9-ac95-c5ead9ee4d19",
      "name": "Respond to Guest Gateway",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1184,
        80
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ ({ status: 'sent', language: $node['Guest Assistant Agent'].json.output.language, escalated: $node['Guest Assistant Agent'].json.output.escalate }) }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "4b468a3d-1283-4cf0-a2e0-4517d6ca61b8",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1648,
        -112
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 1204,
        "content": "## \ud83c\udfe8 Multilingual AI Hotel Guest Assistant\n\nAnswers guest messages from WhatsApp and Viber in their own language (Greek, English, Bulgarian and more), pulls from the hotel knowledge base, escalates anything sensitive to the front desk, and logs every exchange to Google Sheets.\n\n**Perfect for:** hospitality teams running hotels or vacation rentals who want 24/7 guest support without adding headcount.\n\n---\n\n## How it works\n\n1. **Inbound Message Webhook** \u2014 Receives messages from the WhatsApp or Viber gateway.\n2. **Normalize Payload** \u2014 Extracts guest id, channel, sender and message text into a clean shape.\n3. **Guest Assistant Agent** \u2014 LLM agent that detects language, answers from hotel knowledge, and decides when to escalate.\n4. **GPT 4o mini** \u2014 The language model powering the agent.\n5. **Guest Conversation Memory** \u2014 Per guest memory so the bot remembers context across messages.\n6. **Reply Structure Parser** \u2014 Forces clean JSON output (language, reply, escalate, category).\n7. **Needs Human?** \u2014 Routes escalations to staff.\n8. **Notify Front Desk** \u2014 Posts the escalation to the front desk Slack channel.\n9. **Route By Channel** \u2014 Sends the reply back through the right messaging channel.\n10. **Send WhatsApp Reply** / **Send Viber Reply** \u2014 Deliver the answer (with retries on failure).\n11. **Log Conversation** \u2014 Appends every exchange to Google Sheets.\n12. **Respond to Guest Gateway** \u2014 Acknowledges the gateway.\n13. **Error Trigger \u2192 Alert Engineering** \u2014 Catches any failure and alerts the team.\n\n---\n\n## Setup (~15 minutes)\n\n1. **OpenAI** \u2014 Add your key in the *GPT 4o mini* node.\n2. **WhatsApp Cloud API** \u2014 Add header auth in *Send WhatsApp Reply* and set your phone id in the URL.\n3. **Viber** \u2014 Add your bot token header in *Send Viber Reply*.\n4. **Slack** \u2014 Connect your account in *Notify Front Desk* and *Alert Engineering*.\n5. **Google Sheets** \u2014 Connect in *Log Conversation* and set your sheet id.\n> Point your WhatsApp and Viber webhooks at the production URL of *Inbound Message Webhook*. The payload should include channel, from and text."
      },
      "typeVersion": 1
    },
    {
      "id": "3ca01fe3-6a80-4fbd-bf0d-c33297d485e1",
      "name": "Section Intake",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -944,
        -208
      ],
      "parameters": {
        "color": 5,
        "width": 392,
        "height": 464,
        "content": "## 1\ufe0f\u20e3 Intake\n\nThe **Inbound Message Webhook** receives every guest message from the WhatsApp and Viber gateway. **Normalize Payload** flattens the raw body into a consistent structure (guest id, channel, sender, message) so the rest of the workflow never has to care which channel it came from."
      },
      "typeVersion": 1
    },
    {
      "id": "09285c4a-4844-410b-b641-d2769ac535d4",
      "name": "Section AI Core",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        -208
      ],
      "parameters": {
        "color": 3,
        "width": 448,
        "height": 704,
        "content": "## 2\ufe0f\u20e3 AI Core\n\nThe **Guest Assistant Agent** detects the guest language and replies in it, grounded in the hotel knowledge base. **GPT 4o mini** is the model, **Guest Conversation Memory** keeps per guest context, and the **Reply Structure Parser** guarantees a clean JSON answer with an escalate flag the rest of the flow can branch on."
      },
      "typeVersion": 1
    },
    {
      "id": "23467f94-de75-4750-b058-30d122f0e54b",
      "name": "Section Delivery",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -64,
        -256
      ],
      "parameters": {
        "color": 6,
        "width": 904,
        "height": 752,
        "content": "## 3\ufe0f\u20e3 Routing & Delivery\n\n**Needs Human?** checks the escalate flag and, when true, **Notify Front Desk** alerts staff in Slack. **Route By Channel** then sends the reply back through the correct channel using **Send WhatsApp Reply** or **Send Viber Reply**, both of which retry up to three times before giving up so a flaky API call never drops a guest message."
      },
      "typeVersion": 1
    },
    {
      "id": "ea00832a-06f6-4e7f-af71-9306f954d64d",
      "name": "Section Logging",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        -192
      ],
      "parameters": {
        "color": 4,
        "width": 540,
        "height": 760,
        "content": "## 4\ufe0f\u20e3 Logging & Error Handling\n\n**Log Conversation** appends every exchange (language, message, reply, escalation) to Google Sheets for analytics and audit, and **Respond to Guest Gateway** acknowledges the gateway. Separately, the **Error Trigger** fires on any unhandled failure and **Alert Engineering** posts the failing node and error message to the alerts channel so issues are caught fast."
      },
      "typeVersion": 1
    },
    {
      "id": "59a88a25-91e2-480f-8efd-7819f0cac000",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        672,
        48
      ],
      "parameters": {
        "text": "={{ $json.output.reply }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C0AK5VBUE1Z",
          "cachedResultName": "pricing-alerts"
        },
        "otherOptions": {},
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.5
    },
    {
      "id": "23180960-857b-44ca-97a6-8ad554161b6d",
      "name": "Inbound Message Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -896,
        96
      ],
      "parameters": {
        "path": "3a2470dd-bd47-43a3-acd7-a5da9f89166e",
        "options": {}
      },
      "typeVersion": 2.1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "662421f3-9a2d-4da2-8ebb-c01b04baef65",
  "nodeGroups": [],
  "connections": {
    "GPT 4o mini": {
      "ai_languageModel": [
        [
          {
            "node": "Guest Assistant Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Needs Human?": {
      "main": [
        [
          {
            "node": "Notify Front Desk",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Route By Channel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        [
          {
            "node": "Log Conversation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Conversation": {
      "main": [
        [
          {
            "node": "Respond to Guest Gateway",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route By Channel": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Normalize Payload": {
      "main": [
        [
          {
            "node": "Guest Assistant Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Front Desk": {
      "main": [
        [
          {
            "node": "Route By Channel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Guest Assistant Agent": {
      "main": [
        [
          {
            "node": "Needs Human?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reply Structure Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Guest Assistant Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Inbound Message Webhook": {
      "main": [
        [
          {
            "node": "Normalize Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}