{
  "name": "RiadOps",
  "nodes": [
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "gpt-4.1-mini"
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        2976,
        -288
      ],
      "id": "451eb388-a6db-4d8a-adb7-58c9fc79d13a",
      "name": "OpenAI Chat Model"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "gpt-4.1-mini"
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        3712,
        -256
      ],
      "id": "5c64bb2c-39d6-45e1-aff0-173148c19a79",
      "name": "OpenAI Chat Model1"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "riadops/booking-intake",
        "responseMode": "lastNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -64,
        128
      ],
      "id": "48f88709-e4b8-4669-81f3-100e7a98a89e",
      "name": "RO-01 | Booking Event Webhook"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\n\nfunction cleanString(value) {\n  if (value === undefined || value === null) return \"\";\n  return String(value).trim();\n}\n\nfunction normalizeEmail(email) {\n  return cleanString(email).toLowerCase();\n}\n\nfunction normalizePhone(phone) {\n  return cleanString(phone).replace(/\\s+/g, \"\");\n}\n\nfunction normalizeStatus(status) {\n  const value = cleanString(status).toLowerCase();\n\n  const allowed = [\"confirmed\", \"modified\", \"cancelled\"];\n\n  if (allowed.includes(value)) {\n    return value;\n  }\n\n  return \"needs_review\";\n}\n\nfunction normalizeLanguage(language) {\n  const value = cleanString(language).toLowerCase();\n\n  if ([\"fr\", \"en\", \"ar\", \"es\"].includes(value)) {\n    return value;\n  }\n\n  return \"fr\";\n}\n\nfunction toArray(value) {\n  if (Array.isArray(value)) return value;\n  if (!value) return [];\n  return [value];\n}\n\nfunction createGuestId(email, phone) {\n  const key = email || phone || \"unknown\";\n  const cleanKey = key.replace(/[^a-zA-Z0-9]/g, \"\").slice(0, 8).toUpperCase();\n  return `GST-${cleanKey}`;\n}\n\nreturn items.map((item) => {\n  const body = item.json.body || item.json;\n\n  const guest = body.guest || {};\n  const amount = body.amount || {};\n\n  const email = normalizeEmail(guest.email);\n  const phone = normalizePhone(guest.phone);\n  const guestId = createGuestId(email, phone);\n\n  const now = new Date().toISOString();\n\n  return {\n    json: {\n      booking_id: cleanString(body.booking_id),\n      status: normalizeStatus(body.status),\n\n      guest_id: guestId,\n      guest_name: cleanString(guest.name),\n      email,\n      phone,\n      language: normalizeLanguage(guest.language),\n\n      check_in: cleanString(body.check_in),\n      check_out: cleanString(body.check_out),\n\n      adults: Number(body.adults || 0),\n      children: Number(body.children || 0),\n\n      room_type: cleanString(body.room_type),\n\n      amount_value: Number(amount.value || 0),\n      amount_currency: cleanString(amount.currency || \"MAD\").toUpperCase(),\n\n      arrival_time: cleanString(body.arrival_time),\n\n      special_requests: toArray(body.special_requests).join(\", \"),\n\n      source: cleanString(body.source || \"unknown\"),\n\n      journey_status: \"received\",\n\n      created_at: now,\n      updated_at: now,\n\n      raw_booking: body\n    }\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        176,
        128
      ],
      "id": "885458cb-86e4-4c84-934f-3dc1f8339393",
      "name": "RO-02 | Normalize Booking"
    },
    {
      "parameters": {
        "jsCode": "const booking = $json;\n\nconst errors = [];\nconst warnings = [];\nconst missing_fields = [];\n\n// -------------------------\n// Required fields\n// -------------------------\nconst requiredFields = [\n  \"booking_id\",\n  \"status\",\n  \"guest_name\",\n  \"email\",\n  \"check_in\",\n  \"check_out\",\n  \"room_type\"\n];\n\nfor (const field of requiredFields) {\n  const value = booking[field];\n\n  if (\n    value === undefined ||\n    value === null ||\n    String(value).trim() === \"\"\n  ) {\n    missing_fields.push(field);\n  }\n}\n\n// -------------------------\n// Email validation\n// -------------------------\nif (\n  booking.email &&\n  !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(booking.email)\n) {\n  errors.push(\"Invalid email address.\");\n}\n\n// -------------------------\n// Booking Status\n// -------------------------\nconst allowedStatus = [\n  \"confirmed\",\n  \"modified\",\n  \"cancelled\"\n];\n\nif (!allowedStatus.includes(booking.status)) {\n  errors.push(\"Invalid booking status.\");\n}\n\n// -------------------------\n// Dates\n// -------------------------\nif (\n  booking.check_in &&\n  booking.check_out &&\n  new Date(booking.check_out) <= new Date(booking.check_in)\n) {\n  errors.push(\"Check-out date must be after check-in.\");\n}\n\n// -------------------------\n// Adults\n// -------------------------\nif (Number(booking.adults) < 1) {\n  errors.push(\"At least one adult is required.\");\n}\n\n// -------------------------\n// Children\n// -------------------------\nif (Number(booking.children) < 0) {\n  errors.push(\"Children cannot be negative.\");\n}\n\n// -------------------------\n// Amount\n// -------------------------\nif (Number(booking.amount_value) < 0) {\n  errors.push(\"Amount cannot be negative.\");\n}\n\n// -------------------------\n// Arrival Time\n// -------------------------\nif (!booking.arrival_time) {\n  warnings.push(\"Arrival time not provided.\");\n}\n\n// -------------------------\n// Final validation\n// -------------------------\nconst is_valid =\n  missing_fields.length === 0 &&\n  errors.length === 0;\n\nlet validation_status = \"valid\";\n\nif (!is_valid) {\n  validation_status = \"needs_review\";\n} else if (!booking.arrival_time) {\n  validation_status = \"valid_missing_arrival_time\";\n} else if (booking.status === \"cancelled\") {\n  validation_status = \"valid_cancelled_booking\";\n} else if (booking.status === \"modified\") {\n  validation_status = \"valid_modified_booking\";\n} else if (booking.status === \"confirmed\") {\n  validation_status = \"valid_confirmed_booking\";\n}\n\nreturn {\n  json: {\n    ...booking,\n\n    is_valid,\n\n    validation_status,\n\n    errors,\n\n    missing_fields,\n\n    warnings,\n\n    validated_at: new Date().toISOString()\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        384,
        128
      ],
      "id": "86f9a8dc-4b7f-49ca-a51c-fcfffd658297",
      "name": "RO-03 | Validate Booking"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "d85bc6dd-8cff-4a23-92e1-b2a25e90f9ae",
              "leftValue": "={{ $json.is_valid }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        608,
        128
      ],
      "id": "504428fc-e394-4126-b525-34e9399f34b2",
      "name": "RO-04 | Is Booking Valid?"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 3
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "confirmed",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "c4fdbe63-b0d3-4a24-b7e8-e001aa5ff3c2"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Confirmed"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "91068d4b-2435-45bd-8f1f-2feabd730278",
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "modified",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Modified"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "c899cad6-91e4-405f-8fc1-a9edde54f751",
                    "leftValue": "={{ $json.status }}",
                    "rightValue": "cancelled",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Cancelled"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.4,
      "position": [
        832,
        112
      ],
      "id": "d5422f68-65a2-4fc2-91e0-6265f2db571d",
      "name": "RO-05 | Route Booking Status"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "4c9fb4c1-dc9c-4d1e-a7bc-853b95d786da",
              "leftValue": "={{ $json.guest_id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1248,
        -448
      ],
      "id": "a890173a-720d-4b30-a17e-e2651f14778d",
      "name": "RO-07 | Guest Exists?"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_GUESTS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "guests",
          "mode": "name",
          "cachedResultName": "guests"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "email",
              "lookupValue": "={{ $json.email }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1040,
        -448
      ],
      "id": "c6589169-f53a-4d32-be59-574bd1439402",
      "name": "RO-06 | Find Existing Guest",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GUESTS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "guests",
          "mode": "name",
          "cachedResultName": "guests"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "guest_id": "={{ $('RO-05 | Route Booking Status').item.json.guest_id }}",
            "guest_name": "={{ $('RO-05 | Route Booking Status').item.json.guest_name }}",
            "email": "={{ $('RO-05 | Route Booking Status').item.json.email }}",
            "phone": "={{ $('RO-05 | Route Booking Status').item.json.phone }}",
            "language": "={{ $('RO-05 | Route Booking Status').item.json.language }}",
            "created_at": "={{ $('RO-05 | Route Booking Status').item.json.created_at }}",
            "updated_at": "={{ $('RO-05 | Route Booking Status').item.json.updated_at }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "guest_id",
              "displayName": "guest_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "guest_name",
              "displayName": "guest_name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "email",
              "displayName": "email",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "displayName": "phone",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "language",
              "displayName": "language",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "updated_at",
              "displayName": "updated_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1472,
        -384
      ],
      "id": "f970d2fb-855d-4f90-9e88-b0d4a302c187",
      "name": "RO-08 | Create Guest"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_BOOKINGS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "bookings",
          "mode": "name",
          "cachedResultName": "bookings"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "booking_id": "={{ $('RO-05 | Route Booking Status').item.json.booking_id }}",
            "guest_id": "={{ $json.guest_id }}",
            "status": "={{ $('RO-05 | Route Booking Status').item.json.status }}",
            "check_in": "={{ $('RO-05 | Route Booking Status').item.json.check_in }}",
            "check_out": "={{ $('RO-05 | Route Booking Status').item.json.check_out }}",
            "adults": "={{ $('RO-05 | Route Booking Status').item.json.adults }}",
            "children": "={{ $('RO-05 | Route Booking Status').item.json.children }}",
            "room_type": "={{ $('RO-05 | Route Booking Status').item.json.room_type }}",
            "amount_value": "={{ $('RO-05 | Route Booking Status').item.json.amount_value }}",
            "amount_currency": "={{ $('RO-05 | Route Booking Status').item.json.amount_currency }}",
            "arrival_time": "={{ $('RO-05 | Route Booking Status').item.json.arrival_time }}",
            "special_requests": "={{ $('RO-05 | Route Booking Status').item.json.special_requests }}",
            "source": "={{ $('RO-05 | Route Booking Status').item.json.source }}",
            "journey_status": "={{ $('RO-05 | Route Booking Status').item.json.journey_status }}",
            "created_at": "={{ $('RO-05 | Route Booking Status').item.json.created_at }}",
            "updated_at": "={{ $('RO-05 | Route Booking Status').item.json.updated_at }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "booking_id",
              "displayName": "booking_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "guest_id",
              "displayName": "guest_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "check_in",
              "displayName": "check_in",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "check_out",
              "displayName": "check_out",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "adults",
              "displayName": "adults",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "children",
              "displayName": "children",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "room_type",
              "displayName": "room_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "amount_value",
              "displayName": "amount_value",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "amount_currency",
              "displayName": "amount_currency",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "arrival_time",
              "displayName": "arrival_time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "special_requests",
              "displayName": "special_requests",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "source",
              "displayName": "source",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "journey_status",
              "displayName": "journey_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "updated_at",
              "displayName": "updated_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1696,
        -464
      ],
      "id": "bef15b83-9145-4db1-ab53-83c1ba77af07",
      "name": "RO-09 | Store Booking"
    },
    {
      "parameters": {
        "jsCode": "const booking = $json;\n\nfunction addDays(dateString, days) {\n  const d = new Date(dateString);\n  d.setDate(d.getDate() + days);\n  return d.toISOString().split(\"T\")[0];\n}\n\nconst events = [];\n\nevents.push({\n  booking_id: booking.booking_id,\n  event_type: \"pre_arrival_email\",\n  scheduled_date: addDays(booking.check_in, -2),\n  status: \"scheduled\",\n  message_type: \"guest_email\"\n});\n\nevents.push({\n  booking_id: booking.booking_id,\n  event_type: \"arrival_brief\",\n  scheduled_date: booking.check_in,\n  status: \"scheduled\",\n  message_type: \"team_email\"\n});\n\nevents.push({\n  booking_id: booking.booking_id,\n  event_type: \"review_request\",\n  scheduled_date: addDays(booking.check_out, 1),\n  status: \"scheduled\",\n  message_type: \"guest_email\"\n});\n\nreturn events.map((event, index) => ({\n  json: {\n    event_id: `EVT-${booking.booking_id}-${index + 1}`,\n    created_at: new Date().toISOString(),\n    sent_at: \"\",\n    ...event\n  }\n}));"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1904,
        -464
      ],
      "id": "142bb669-bc45-4bba-9980-d4ee32ce0eb5",
      "name": "RO-10 | Build Journey Schedule"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_JOURNEY_EVENTS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "journey_events",
          "mode": "name",
          "cachedResultName": "journey_events"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "event_id": "={{ $json.event_id }}",
            "booking_id": "={{ $json.booking_id }}",
            "event_type": "={{ $json.event_type }}",
            "scheduled_date": "={{ $json.scheduled_date }}",
            "status": "={{ $json.status }}",
            "message_type": "={{ $json.message_type }}",
            "created_at": "={{ $json.created_at }}",
            "sent_at": "={{ $json.sent_at }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "event_id",
              "displayName": "event_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "booking_id",
              "displayName": "booking_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "event_type",
              "displayName": "event_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "scheduled_date",
              "displayName": "scheduled_date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "message_type",
              "displayName": "message_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "sent_at",
              "displayName": "sent_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2112,
        -464
      ],
      "id": "3b322d3d-2ec1-4c28-ad9b-a048817fda95",
      "name": "RO-11 | Store Journey Events"
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nconst output = [];\n\nfor (const item of items) {\n\n  const e = item.json;\n\n  let owner = \"Reception\";\n  let title = \"\";\n  let description = \"\";\n\n  switch (e.event_type) {\n\n    case \"pre_arrival_email\":\n      owner = \"Reception\";\n      title = \"Pr\u00e9parer l'arriv\u00e9e du client\";\n      description = \"V\u00e9rifier les informations avant l'arriv\u00e9e.\";\n      break;\n\n    case \"arrival_brief\":\n      owner = \"Operations\";\n      title = \"Pr\u00e9parer la chambre\";\n      description = \"Pr\u00e9parer la chambre avant le check-in.\";\n      break;\n\n    case \"review_request\":\n      owner = \"Marketing\";\n      title = \"Suivi apr\u00e8s s\u00e9jour\";\n      description = \"V\u00e9rifier l'envoi de la demande d'avis.\";\n      break;\n\n  }\n\n  output.push({\n\n    json: {\n\n      task_id: `TASK-${e.event_id}`,\n\n      booking_id: e.booking_id,\n\n      task_type: e.event_type,\n\n      task_title: title,\n\n      task_description: description,\n\n      due_date: e.scheduled_date,\n\n      owner,\n\n      status: \"open\",\n\n      created_at: new Date().toISOString()\n\n    }\n\n  });\n\n}\n\nreturn output;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2320,
        -464
      ],
      "id": "22e90b14-f167-4486-9f87-a574d66c5c16",
      "name": "RO-12 | Build Internal Tasks"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_INTERNAL_TASKS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "internal_tasks",
          "mode": "name",
          "cachedResultName": "internal_tasks"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "task_id": "={{ $json.task_id }}",
            "booking_id": "={{ $json.booking_id }}",
            "task_type": "={{ $json.task_type }}",
            "task_title": "={{ $json.task_title }}",
            "task_description": "={{ $json.task_description }}",
            "due_date": "={{ $json.due_date }}",
            "owner": "={{ $json.owner }}",
            "status": "={{ $json.status }}",
            "created_at": "={{ $json.created_at }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "task_id",
              "displayName": "task_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "booking_id",
              "displayName": "booking_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "task_type",
              "displayName": "task_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "task_title",
              "displayName": "task_title",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "task_description",
              "displayName": "task_description",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "due_date",
              "displayName": "due_date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "owner",
              "displayName": "owner",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2528,
        -464
      ],
      "id": "e42834f1-6ee7-4bf3-8225-538a75169fd4",
      "name": "RO-13 | Store Internal Tasks"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Booking ID:\n{{ $('RO-02 | Normalize Booking').item.json.booking_id }}\n\nGuest:\n{{ $('RO-02 | Normalize Booking').item.json.guest_name }}\n\nSpecial Requests:\n{{ $('RO-02 | Normalize Booking').item.json.special_requests }}\n\nArrival:\n{{ $('RO-02 | Normalize Booking').item.json.arrival_time }}\n\nRoom:\n{{ $('RO-02 | Normalize Booking').item.json.room_type }}",
        "messages": {
          "messageValues": [
            {
              "message": "You are an AI Concierge Assistant for a luxury riad.  Your role is to analyze guest booking information and special requests, then provide actionable recommendations for the hotel staff.  Your response will be used by an automation workflow, so it MUST always be valid JSON.  Rules:  - Return ONLY valid JSON. - Do NOT use Markdown. - Do NOT wrap the JSON inside ```json. - Do NOT add explanations, comments, or extra text. - Always return exactly one JSON object. - If there are no special requests, still return a valid JSON object. - Be concise and practical. - Focus on operational actions for the staff.  Priority levels: - low - medium - high  Departments: - Reception - Housekeeping - Kitchen - Management - Maintenance  Determine the priority based on the guest's requests.  Examples:  Airport transfer \u2192 Reception Late check-in \u2192 Reception Extra cleaning \u2192 Housekeeping Vegan breakfast \u2192 Kitchen Birthday / Honeymoon \u2192 Management Maintenance issue \u2192 Maintenance  The JSON schema is:  {   \"priority\": \"low | medium | high\",   \"department\": \"Reception | Housekeeping | Kitchen | Management | Maintenance\",   \"summary\": \"Short operational summary for the staff.\",   \"recommended_actions\": [     \"Action 1\",     \"Action 2\",     \"Action 3\"   ] }  The summary must be less than 30 words.  Each recommended action must: - be short; - start with a verb; - be immediately actionable; - contain no numbering.  Never invent information that is not present in the booking."
            }
          ]
        },
        "batching": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        3008,
        -464
      ],
      "id": "da2ff77a-f79d-4e13-96f7-67c85da17fc7",
      "name": "RO-14 | AI Guest Request Analyzer"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let text = $json.text || \"\";\n\n// Supprime les balises Markdown\ntext = text\n  .replace(/```json/g, \"\")\n  .replace(/```/g, \"\")\n  .trim();\n\nlet parsed;\n\ntry {\n  parsed = JSON.parse(text);\n} catch (e) {\n  throw new Error(\"Invalid JSON returned by AI:\\n\" + text);\n}\n\nreturn {\n  json: {\n    analysis_id: `AI-${Date.now()}`,\n    booking_id: $('RO-02 | Normalize Booking').item.json.booking_id,\n\n    priority: parsed.priority,\n\n    department: parsed.department,\n\n    summary: parsed.summary,\n\n    recommended_actions: parsed.recommended_actions,\n\n    created_at: new Date().toISOString()\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3360,
        -464
      ],
      "id": "14ed376e-0546-4f77-a354-827a7e33bc23",
      "name": "RO-15 | Parse AI Analysis"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GUEST_AI_ANALYSIS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "guest_ai_analysis",
          "mode": "name",
          "cachedResultName": "guest_ai_analysis"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "analysis_id": "={{ $json.analysis_id }}",
            "booking_id": "={{ $json.booking_id }}",
            "priority": "={{ $json.priority }}",
            "department": "={{ $json.department }}",
            "summary": "={{ $json.summary }}",
            "recommended_actions": "={{ $json.recommended_actions }}",
            "created_at": "={{ $json.created_at }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "analysis_id",
              "displayName": "analysis_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "booking_id",
              "displayName": "booking_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "priority",
              "displayName": "priority",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "department",
              "displayName": "department",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "summary",
              "displayName": "summary",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "recommended_actions",
              "displayName": "recommended_actions",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        3568,
        -464
      ],
      "id": "0a2eb542-82e4-4d70-92a0-1ac2ad008918",
      "name": "RO-16 | Save AI Analysis"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Booking ID:\n{{ $('RO-02 | Normalize Booking').item.json.booking_id }}\n\nGuest Name:\n{{ $('RO-02 | Normalize Booking').item.json.guest_name }}\n\nRoom:\n{{ $('RO-02 | Normalize Booking').item.json.room_type }}\n\nCheck-in:\n{{ $('RO-02 | Normalize Booking').item.json.check_in }}\n\nArrival Time:\n{{ $('RO-02 | Normalize Booking').item.json.arrival_time }}\n\nSpecial Requests:\n{{ $('RO-02 | Normalize Booking').item.json.special_requests }}\n\nAI Summary:\n{{ $('RO-15 | Parse AI Analysis').item.json.summary }}",
        "messages": {
          "messageValues": [
            {
              "message": "You are the concierge of a luxury Moroccan riad.  Write a warm, elegant and professional pre-arrival email.  Your objective is to welcome the guest and reassure them before arrival.  Rules:  - Return ONLY valid JSON. - Do not use markdown. - Do not use ```json. - Do not add explanations. - Keep the email under 180 words. - Use a warm and welcoming tone. - Mention only information available in the booking. - If there are no special requests, simply welcome the guest. - If there are special requests, reassure the guest that they have been noted. - Never invent information.  Return exactly this schema:  {   \"subject\": \"\",   \"body\": \"\" }"
            }
          ]
        },
        "batching": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        3776,
        -464
      ],
      "id": "6bc3bf13-9fb9-48e7-81bd-11b20577f960",
      "name": "RO-17 | Generate Guest Email"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "let text = $json.text || \"\";\n\ntext = text\n  .replace(/```json/g, \"\")\n  .replace(/```/g, \"\")\n  .trim();\n\nconst email = JSON.parse(text);\n\nreturn {\n  json: {\n    subject: email.subject,\n    body: email.body,\n    booking_id: $('RO-02 | Normalize Booking').item.json.booking_id,\n    guest_name: $('RO-02 | Normalize Booking').item.json.guest_name,\n    email: $('RO-02 | Normalize Booking').item.json.email\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        4128,
        -464
      ],
      "id": "94d179f8-4f34-4564-9304-5656e21a0d42",
      "name": "RO-18 | Parse Guest Email"
    },
    {
      "parameters": {
        "sendTo": "={{ $('RO-02 | Normalize Booking').item.json.email }}",
        "subject": "={{ $json.subject }}",
        "emailType": "text",
        "message": "={{ $json.body }}",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        4336,
        -464
      ],
      "id": "6521da07-cfdc-4646-ab9b-8d0f83ff2503",
      "name": "RO-19 | Send Guest Email"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_BOOKINGS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "bookings",
          "mode": "name",
          "cachedResultName": "bookings"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "booking_id",
              "lookupValue": "={{ $json.booking_id }}"
            }
          ]
        },
        "options": {
          "returnFirstMatch": true
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1136,
        80
      ],
      "id": "d8a8c5f4-de77-4dc2-85f9-6895cc4931c8",
      "name": "RO-20 | Find Existing Booking"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "2c3460de-eef9-4368-9eda-dcf4cc6574a2",
              "leftValue": "={{ $json.booking_id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1360,
        -32
      ],
      "id": "a5181713-848c-4d77-b3ac-2f79c0be18ce",
      "name": "RO-21 | Booking Exists?"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "YOUR_BOOKINGS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "bookings",
          "mode": "name",
          "cachedResultName": "bookings"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "booking_id": "={{ $json.booking_id }}",
            "guest_id": "={{ $json.guest_id }}",
            "status": "={{ $('RO-05 | Route Booking Status').item.json.status }}",
            "check_in": "={{ $('RO-05 | Route Booking Status').item.json.check_in }}",
            "check_out": "={{ $('RO-05 | Route Booking Status').item.json.check_out }}",
            "adults": "={{ $json.adults }}",
            "children": "={{ $('RO-05 | Route Booking Status').item.json.children }}",
            "room_type": "={{ $('RO-05 | Route Booking Status').item.json.room_type }}",
            "amount_value": "={{ $('RO-05 | Route Booking Status').item.json.amount_value }}",
            "amount_currency": "={{ $json.amount_currency }}",
            "arrival_time": "={{ $('RO-05 | Route Booking Status').item.json.arrival_time }}",
            "special_requests": "={{ $('RO-05 | Route Booking Status').item.json.special_requests }}",
            "source": "={{ $('RO-05 | Route Booking Status').item.json.source }}",
            "journey_status": "={{ $('RO-05 | Route Booking Status').item.json.journey_status }}",
            "created_at": "={{ $('RO-05 | Route Booking Status').item.json.created_at }}",
            "updated_at": "={{ $('RO-05 | Route Booking Status').item.json.updated_at }}"
          },
          "matchingColumns": [
            "booking_id"
          ],
          "schema": [
            {
              "id": "booking_id",
              "displayName": "booking_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "guest_id",
              "displayName": "guest_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "check_in",
              "displayName": "check_in",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "check_out",
              "displayName": "check_out",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "adults",
              "displayName": "adults",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "children",
              "displayName": "children",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "room_type",
              "displayName": "room_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "amount_value",
              "displayName": "amount_value",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "amount_currency",
              "displayName": "amount_currency",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "arrival_time",
              "displayName": "arrival_time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "special_requests",
              "displayName": "special_requests",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "source",
              "displayName": "source",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "journey_status",
              "displayName": "journey_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "updated_at",
              "displayName": "updated_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1616,
        -96
      ],
      "id": "b00c4d1a-07b0-4e97-9431-31e0541ede7a",
      "name": "RO-22 | Update Booking"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Booking ID: {{$json.booking_id}}\n\nGuest:\n{{$json.guest_name}}\n\nRoom:\n{{$json.room_type}}\n\nCheck-in:\n{{$json.check_in}}\n\nCheck-out:\n{{$json.check_out}}\n\nAdults:\n{{$json.adults}}\n\nChildren:\n{{$json.children}}\n\nArrival time:\n{{$json.arrival_time}}\n\nSpecial Requests:\n{{$json.special_requests}}\n\nReservation Status:\nModified\n\nAnalyze this updated reservation.",
        "messages": {
          "messageValues": [
            {
              "message": "You are an AI Concierge Assistant for a luxury Moroccan riad.  Analyze the updated reservation.  Determine:  - priority (Low, Medium, High) - department responsible - short summary - recommended actions  Return ONLY valid JSON.  {   \"priority\": \"\",   \"department\": \"\",   \"summary\": \"\",   \"recommended_actions\": [] }"
            }
          ]
        },
        "batching": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        1824,
        -64
      ],
      "id": "c198c9e5-602a-44dd-ba44-c7083ad4b778",
      "name": "RO-23 | AI Guest Request Analyzer"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "gpt-4.1-mini"
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        1760,
        192
      ],
      "id": "c50e0f6d-67fb-4a5a-81fd-ccee75a886c6",
      "name": "OpenAI Chat Model2"
    },
    {
      "parameters": {
        "jsCode": "let output = $json.output || $json.text || $json.response || \"\";\n\noutput = output\n  .replace(/```json/g, \"\")\n  .replace(/```/g, \"\")\n  .trim();\n\nconst analysis = JSON.parse(output);\n\nreturn {\n  json: {\n    booking_id: $('RO-22 | Update Booking').first().json.booking_id,\n\n    priority: analysis.priority,\n\n    department: analysis.department,\n\n    summary: analysis.summary,\n\n    recommended_actions: Array.isArray(analysis.recommended_actions)\n      ? analysis.recommended_actions.join(\", \")\n      : analysis.recommended_actions,\n\n    updated_at: new Date().toISOString()\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2176,
        -16
      ],
      "id": "89f3c8cf-f8a5-4a89-a958-0917f3a68d72",
      "name": "RO-24 | Parse AI Analysis"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_GUEST_AI_ANALYSIS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "guest_ai_analysis",
          "mode": "name",
          "cachedResultName": "guest_ai_analysis"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "booking_id",
              "lookupValue": "={{ $('RO-21 | Booking Exists?').item.json.booking_id }}"
            }
          ]
        },
        "options": {
          "returnFirstMatch": true
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2384,
        -16
      ],
      "id": "e2881750-9e87-4739-8a57-3fe0e26fa0b5",
      "name": "RO-25 | Find AI Analysis"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "YOUR_GUEST_AI_ANALYSIS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "guest_ai_analysis",
          "mode": "name",
          "cachedResultName": "guest_ai_analysis"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "analysis_id": "={{ $json.analysis_id }}",
            "booking_id": "={{ $json.booking_id }}",
            "priority": "={{ $json.priority }}",
            "department": "={{ $json.department }}",
            "summary": "={{ $json.summary }}",
            "recommended_actions": "={{ $json.recommended_actions }}",
            "created_at": "={{ $json.created_at }}"
          },
          "matchingColumns": [
            "analysis_id"
          ],
          "schema": [
            {
              "id": "analysis_id",
              "displayName": "analysis_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "booking_id",
              "displayName": "booking_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "priority",
              "displayName": "priority",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "department",
              "displayName": "department",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "summary",
              "displayName": "summary",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "recommended_actions",
              "displayName": "recommended_actions",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2592,
        -16
      ],
      "id": "e572ea33-9633-480f-841b-14cba7a74e7f",
      "name": "RO-26 | Update AI Analysis"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Guest: {{$('RO-22 | Update Booking').first().json.guest_name}}\n\nBooking ID:\n{{$('RO-22 | Update Booking').first().json.booking_id}}\n\nCheck-in:\n{{$('RO-22 | Update Booking').first().json.check_in}}\n\nCheck-out:\n{{$('RO-22 | Update Booking').first().json.check_out}}\n\nRoom:\n{{$('RO-22 | Update Booking').first().json.room_type}}\n\nArrival:\n{{$('RO-22 | Update Booking').first().json.arrival_time}}\n\nSpecial Requests:\n{{$('RO-22 | Update Booking').first().json.special_requests}}\n\nWrite the updated confirmation email.",
        "messages": {
          "messageValues": [
            {
              "message": "You are the concierge of a luxury Moroccan riad.  Write a professional email informing the guest that their reservation has been successfully updated.  Return ONLY valid JSON.  {   \"subject\":\"\",   \"body\":\"\" }"
            }
          ]
        },
        "batching": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        2800,
        -16
      ],
      "id": "90b05556-7015-4301-a015-d0481352a831",
      "name": "RO-27 | Generate Updated Guest Email"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "gpt-4.1-mini"
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        2736,
        192
      ],
      "id": "b53b42ff-0111-4bbe-b98d-1b31bdb365ae",
      "name": "OpenAI Chat Model3"
    },
    {
      "parameters": {
        "jsCode": "let text = $json.text || \"\";\n\ntext = text\n  .replace(/```json/g, \"\")\n  .replace(/```/g, \"\")\n  .trim();\n\nconst email = JSON.parse(text);\n\nreturn {\n  json: {\n    subject: email.subject,\n    body: email.body,\n    booking_id: $('RO-02 | Normalize Booking').item.json.booking_id,\n    guest_name: $('RO-02 | Normalize Booking').item.json.guest_name,\n    email: $('RO-02 | Normalize Booking').item.json.email\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3152,
        -16
      ],
      "id": "98e22f9b-9d7e-4a36-90cb-86135827a2ff",
      "name": "RO-28 | Parse Updated Guest Email"
    },
    {
      "parameters": {
        "sendTo": "={{ $('RO-02 | Normalize Booking').item.json.email }}",
        "subject": "={{ $json.subject }}",
        "emailType": "text",
        "message": "={{ $json.body }}",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        3360,
        -16
      ],
      "id": "6bbe8209-78f9-4c83-83e3-727c226e6bfc",
      "name": "RO-29 | Send Updated Guest Email"
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "value": "YOUR_BOOKINGS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "bookings",
          "mode": "name",
          "cachedResultName": "bookings"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "booking_id",
              "lookupValue": "={{ $json.booking_id }}"
            }
          ]
        },
        "options": {
          "returnFirstMatch": true
        }
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1152,
        464
      ],
      "id": "39f2f363-43b3-435e-8d1e-541fbdb9d5fc",
      "name": "RO-30 | Find Booking"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "2c3460de-eef9-4368-9eda-dcf4cc6574a2",
              "leftValue": "={{ $json.booking_id }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        1424,
        480
      ],
      "id": "56fdb848-f989-46f5-afb8-0e5497060c25",
      "name": "RO-31 | Booking Exists?"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Guest Name:\n{{$('RO-32 | Update Booking Status').first().json.guest_name}}\n\nBooking ID:\n{{$('RO-32 | Update Booking Status').first().json.booking_id}}\n\nCheck-in:\n{{$('RO-32 | Update Booking Status').first().json.check_in}}\n\nCheck-out:\n{{$('RO-32 | Update Booking Status').first().json.check_out}}\n\nCancellation Reason:\n{{$('RO-32 | Update Booking Status').first().json.cancellation_reason}}\n\nWrite a professional cancellation confirmation email.",
        "messages": {
          "messageValues": [
            {
              "message": "You are the concierge of a luxury Moroccan riad.  A guest has cancelled their reservation.  Write a warm, professional confirmation email.  Return ONLY valid JSON.  {   \"subject\":\"\",   \"body\":\"\" }"
            }
          ]
        },
        "batching": {}
      },
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        1888,
        416
      ],
      "id": "233b3316-a697-4bdf-841e-2c2c19e8c236",
      "name": "RO-33 | Generate Cancellation Email"
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "value": "YOUR_BOOKINGS_SPREADSHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "bookings",
          "mode": "name",
          "cachedResultName": "bookings"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "status": "cancelled",
            "updated_at": "={{$now}}",
            "booking_id": "={{ $json.booking_id }}"
          },
          "matchingColumns": [
            "booking_id"
          ],
          "schema": [
            {
              "id": "booking_id",
              "displayName": "booking_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "guest_id",
              "displayName": "guest_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "check_in",
              "displayName": "check_in",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "check_out",
              "displayName": "check_out",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "adults",
              "displayName": "adults",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "children",
              "displayName": "children",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "room_type",
              "displayName": "room_type",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "amount_value",
              "displayName": "amount_value",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "amount_currency",
              "displayName": "amount_currency",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "arrival_time",
              "displayName": "arrival_time",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "special_requests",
              "displayName": "special_requests",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "source",
              "displayName": "source",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "journey_status",
              "displayName": "journey_status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "created_at",
              "displayName": "created_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": true
            },
            {
              "id": "updated_at",
              "displayName": "updated_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "row_number",
              "displayName": "row_number",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "number",
              "canBeUsedToMatch": true,
              "readOnly": true,
              "removed": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        1680,
        416
      ],
      "id": "e4399bf6-858f-473c-982b-c85372c188ed",
      "name": "RO-32 | Update Booking Status"
    },
    {
      "parameters": {
        "model": {
          "__rl": true,
          "value": "gpt-4.1-mini",
          "mode": "list",
          "cachedResultName": "gpt-4.1-mini"
        },
        "builtInTools": {},
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "typeVersion": 1.3,
      "position": [
        1824,
        624
      ],
      "id": "f0421971-a8b2-46ee-8ebe-9416dd72106d",
      "name": "OpenAI Chat Model4"
    },
    {
      "parameters": {
        "jsCode": "let output = $json.output || $json.text || \"\";\n\noutput = output\n  .replace(/```json/g, \"\")\n  .replace(/```/g, \"\")\n  .trim();\n\nconst email = JSON.parse(output);\n\nreturn {\n  json: {\n    booking_id: $('RO-32 | Update Booking Status').first().json.booking_id,\n    guest_name: $('RO-32 | Update Booking Status').first().json.guest_name,\n    email: $('RO-32 | Update Booking Status').first().json.email,\n    subject: email.subject,\n    body: email.body\n  }\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2240,
        416
      ],
      "id": "624db837-6d48-4a42-b114-8e4d0f62726a",
      "name": "RO-34 | Parse Cancellation Email"
    },
    {
      "parameters": {
        "sendTo": "={{ $('RO-02 | Normalize Booking').item.json.email }}",
        "subject": "={{$json.subject}}",
        "message": "={{$json.body}}",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        2448,
        416
      ],
      "id": "d8b65d93-74e0-45c1-9841-42ea6ca7acf6",
      "name": "RO-35 | Send Cancellation Email"
    },
    {
      "parameters": {
        "jsCode": "const booking = $(\"RO-02 | Normalize Booking\").first().json;\n\nreturn [\n  {\n    json: booking\n  }\n];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2736,
        -464
      ],
      "id": "8f83d733-fbec-45d5-81c6-b56b356cf435",
      "name": "RO-13.5 | Restore Booking Context"
    }
  ],
  "connections": {
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "RO-14 | AI Guest Request Analyzer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "RO-17 | Generate Guest Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "RO-01 | Booking Event Webhook": {
      "main": [
        [
          {
            "node": "RO-02 | Normalize Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-02 | Normalize Booking": {
      "main": [
        [
          {
            "node": "RO-03 | Validate Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-03 | Validate Booking": {
      "main": [
        [
          {
            "node": "RO-04 | Is Booking Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-04 | Is Booking Valid?": {
      "main": [
        [
          {
            "node": "RO-05 | Route Booking Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-05 | Route Booking Status": {
      "main": [
        [
          {
            "node": "RO-06 | Find Existing Guest",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "RO-20 | Find Existing Booking",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "RO-30 | Find Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-07 | Guest Exists?": {
      "main": [
        [
          {
            "node": "RO-09 | Store Booking",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "RO-08 | Create Guest",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-06 | Find Existing Guest": {
      "main": [
        [
          {
            "node": "RO-07 | Guest Exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-08 | Create Guest": {
      "main": [
        [
          {
            "node": "RO-09 | Store Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-09 | Store Booking": {
      "main": [
        [
          {
            "node": "RO-10 | Build Journey Schedule",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-10 | Build Journey Schedule": {
      "main": [
        [
          {
            "node": "RO-11 | Store Journey Events",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-11 | Store Journey Events": {
      "main": [
        [
          {
            "node": "RO-12 | Build Internal Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-12 | Build Internal Tasks": {
      "main": [
        [
          {
            "node": "RO-13 | Store Internal Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-13 | Store Internal Tasks": {
      "main": [
        [
          {
            "node": "RO-13.5 | Restore Booking Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-14 | AI Guest Request Analyzer": {
      "main": [
        [
          {
            "node": "RO-15 | Parse AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-15 | Parse AI Analysis": {
      "main": [
        [
          {
            "node": "RO-16 | Save AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-16 | Save AI Analysis": {
      "main": [
        [
          {
            "node": "RO-17 | Generate Guest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-17 | Generate Guest Email": {
      "main": [
        [
          {
            "node": "RO-18 | Parse Guest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-18 | Parse Guest Email": {
      "main": [
        [
          {
            "node": "RO-19 | Send Guest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-20 | Find Existing Booking": {
      "main": [
        [
          {
            "node": "RO-21 | Booking Exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-21 | Booking Exists?": {
      "main": [
        [
          {
            "node": "RO-22 | Update Booking",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-22 | Update Booking": {
      "main": [
        [
          {
            "node": "RO-23 | AI Guest Request Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "RO-23 | AI Guest Request Analyzer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "RO-23 | AI Guest Request Analyzer": {
      "main": [
        [
          {
            "node": "RO-24 | Parse AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-24 | Parse AI Analysis": {
      "main": [
        [
          {
            "node": "RO-25 | Find AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-25 | Find AI Analysis": {
      "main": [
        [
          {
            "node": "RO-26 | Update AI Analysis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-26 | Update AI Analysis": {
      "main": [
        [
          {
            "node": "RO-27 | Generate Updated Guest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model3": {
      "ai_languageModel": [
        [
          {
            "node": "RO-27 | Generate Updated Guest Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "RO-27 | Generate Updated Guest Email": {
      "main": [
        [
          {
            "node": "RO-28 | Parse Updated Guest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-28 | Parse Updated Guest Email": {
      "main": [
        [
          {
            "node": "RO-29 | Send Updated Guest Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-30 | Find Booking": {
      "main": [
        [
          {
            "node": "RO-31 | Booking Exists?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-31 | Booking Exists?": {
      "main": [
        [
          {
            "node": "RO-32 | Update Booking Status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-32 | Update Booking Status": {
      "main": [
        [
          {
            "node": "RO-33 | Generate Cancellation Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model4": {
      "ai_languageModel": [
        [
          {
            "node": "RO-33 | Generate Cancellation Email",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "RO-33 | Generate Cancellation Email": {
      "main": [
        [
          {
            "node": "RO-34 | Parse Cancellation Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-34 | Parse Cancellation Email": {
      "main": [
        [
          {
            "node": "RO-35 | Send Cancellation Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RO-13.5 | Restore Booking Context": {
      "main": [
        [
          {
            "node": "RO-14 | AI Guest Request Analyzer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "nodeGroups": [],
  "tags": []
}