{
  "meta": {
    "instanceId": "473168f370e695cf2681bfe0d87794a129a4b6b91ce9730668db1c27be277998"
  },
  "nodes": [
    {
      "id": "a1bb9718-a390-4b5f-8534-b8a973ce8695",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -3104,
        1984
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 1
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "33e9808c-510f-4b1c-883e-f0a569248ab7",
      "name": "Append or update slot",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2496,
        1696
      ],
      "parameters": {
        "columns": {
          "value": {
            "day": "={{ $json.days }}",
            "time": "={{ $json.time }}",
            "service": "={{ $json.service }}"
          },
          "schema": [
            {
              "id": "service",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "service",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "day",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "day",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "service"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit#gid=0",
          "cachedResultName": "slots"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit?usp=drivesdk",
          "cachedResultName": "CRM"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "fdd67b6a-4301-4fff-bf04-5fd448f768db",
      "name": "Get lead",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -2704,
        1888
      ],
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "16bAoJicIDEPak46",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/16bAoJicIDEPak46",
          "cachedResultName": "leads"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a96ea4f0-f1c0-42c3-813b-7fc6ac28ac0d",
      "name": "Append or update lead",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2496,
        1888
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $json.name }}",
            "phone": "={{ $json.phone }}",
            "short sum": "={{ $json.short_sum }}",
            "lead category": "={{ $json.lead_category }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "lead category",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "lead category",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "short sum",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "short sum",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "phone"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1596543480,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit#gid=1596543480",
          "cachedResultName": "leads"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit?usp=drivesdk",
          "cachedResultName": "CRM"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "8bf64824-9d63-4c35-a9ff-0a23fd927bb7",
      "name": "Get human call",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -2704,
        2064
      ],
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "ygicufEiNou6tVCb",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/ygicufEiNou6tVCb",
          "cachedResultName": "human"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "aa6042af-187c-4326-bfe5-d46e783acdfa",
      "name": "Append or update human call",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2496,
        2064
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $json.name }}",
            "phone": "={{ $json.phone }}",
            "description": "={{ $json.descreption }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "phone"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 292004918,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit#gid=292004918",
          "cachedResultName": "human call"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit?usp=drivesdk",
          "cachedResultName": "CRM"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "9f4c7fec-7444-4b95-aa2c-0cfc70903c1c",
      "name": "Get human appointment",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -2688,
        2240
      ],
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "XICUlFpXl76eV2Dm",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/XICUlFpXl76eV2Dm",
          "cachedResultName": "appointments"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9ae86536-5815-4b76-bc4d-e1371866bcda",
      "name": "Append or update appointment",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2496,
        2240
      ],
      "parameters": {
        "columns": {
          "value": {
            "day": "={{ $json.day }}",
            "date": "={{ $json.date }}",
            "name": "={{ $json.name }}",
            "time": "={{ $json.times }}",
            "phone": "={{ $json.phone }}",
            "service": "={{ $json.service }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "name",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "time",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "time",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "day",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "day",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "service",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "service",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "phone"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1977548840,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit#gid=1977548840",
          "cachedResultName": "appointments"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit?usp=drivesdk",
          "cachedResultName": "CRM"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credential-id",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "7685907b-6ffe-43f2-8963-eac68e4ff6f4",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        -3248,
        1456
      ],
      "parameters": {
        "color": 7,
        "width": 1296,
        "height": 1168,
        "content": "## SENDING new rows to google sheet"
      },
      "typeVersion": 1
    },
    {
      "id": "5577ae1c-5d66-4905-8fa3-71d4d7d2e283",
      "name": "Booked Appointment",
      "type": "n8n-nodes-base.dataTableTool",
      "position": [
        -864,
        880
      ],
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "XICUlFpXl76eV2Dm",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/XICUlFpXl76eV2Dm",
          "cachedResultName": "appointments"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "1e612891-e43a-4153-8098-22f0326a3883",
      "name": "AI Summarizer1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        128,
        1264
      ],
      "parameters": {
        "text": "={{ $('Format Context').item.json.formatted_output }}",
        "options": {
          "systemMessage": "=# Customer Message Analyzer\n\nAnalyze customer messages and return only JSON:\n```json\n{\"summary\": \"brief description\", \"category\": \"category_name\"}\n```\n\n## Categories\n- **appointment**: booking request\n- **pricing**: price inquiry\n- **discount**: discount request\n- **teeth_whitening**: whitening inquiry\n- **skincare**: skincare inquiry\n- **staff_request**: staff contact request\n- **out_of_scope**: unrelated topic\n\n## Instructions\n- Summary: one clear sentence describing the customer's request\n- Output: JSON only, no additional text\n- answer with the user's language "
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "c0e92bbc-42cb-4a40-b403-27150e290bb2",
      "name": "Check Appointment1",
      "type": "n8n-nodes-base.if",
      "position": [
        304,
        352
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.has_appointment }}",
              "rightValue": "YES"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "98e8a42a-9e2c-4468-87f4-8f7b441a94d3",
      "name": "AI Data Extractor",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -208,
        352
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "options": {
          "systemMessage": "=// System Prompt for AI Data Extractor Node\n-- today is {{ $now }}\nYou are a precise data extractor. Extract information in JSON format ONLY.\n-- **if there was no date**  return the correct date of the nearest day that was chosen. \n⚠️ Return ONLY valid JSON, no markdown or extra text.\n\n📋 Required Format:\n{\n  \"has_appointment\": true or false,\n  \"is_reschedule\": true or false,\n  \"needs_human\": true or false,\n  \"appointment_date\": \"YYYY-MM-DD\" or null,\n  \"appointment_day\": \"الأحد/الاثنين/etc\" or null,\n  \"appointment_time\": \"HH:MM\" or null,\n  \"service\": \"service name in Arabic\" or null,\n  \"lead_category\": \"only one of those booking/reschedule/pricing/discount/teeth_whitening/skin_care/ask_agent/out_of_scope/other\",\n  \"description\": \"brief description in Arabic\",\n  \"notes\": \"additional notes\" or null\n}\n\n📝 Rules:\n**has_appointment:**\n- true if booking confirmed: \"تم حجز موعدك\" OR \"تم تعديل موعدك\"\n- false otherwise\n\n**is_reschedule:**\n- true if customer changed existing appointment\n- false for new bookings\n\n**needs_human:**\n⚠️ BE VERY STRICT - only true if:\n- EXPLICIT request: \"بدي موظف\" / \"وديني على حدا\" / \"بدي أحكي مع شخص\" / \"اتصل فيني\"\n- Clear anger/frustration: cursing, multiple complaints, \"مش راضي\" / \"زهقت\"\n- Medical emergency: \"وجع شديد\" / \"حالة طارئة\" / \"مريض كثير\"\n- Bot explicitly failed multiple times\n\n⛔ DO NOT set true for:\n- Simple questions about services\n- Asking about prices or discounts\n- General inquiries (\"شو عندكم؟\" / \"كيف؟\")\n- Booking/rescheduling requests\n- Normal conversation\n\n**appointment_date/day/time/service:**\n- Extract as before\n- null if not mentioned\n\n**lead_category:**\n- \"reschedule\": if modifying existing appointment\n- \"booking\": new appointment booking\n- \"pricing\": price inquiry\n- \"discount\": discount request\n- \"teeth_whitening\": teeth whitening inquiry\n- \"skin_care\": skin care inquiry\n- \"ask_agent\": wants to speak with staff (ONLY if explicit request)\n- \"out_of_scope\": outside clinic specialty\n- \"other\": anything else\n\n**description:**\n- One sentence in Arabic\n- Examples:\n  - \"العميل قام بتعديل موعده لتبييض الأسنان\"\n  - \"طلب تغيير الموعد من الأحد إلى الثلاثاء\"\n  - \"طلب التحدث مع موظف بشكل صريح\"\n\n🎯 Example - Normal Question (needs_human = FALSE):\nUser: \"شو عندكم خدمات؟\"\n{\n  \"has_appointment\": false,\n  \"is_reschedule\": false,\n  \"needs_human\": false,\n  \"appointment_date\": null,\n  \"appointment_day\": null,\n  \"appointment_time\": null,\n  \"service\": null,\n  \"lead_category\": \"other\",\n  \"description\": \"استفسار عام عن الخدمات\",\n  \"notes\": null\n}\n\n🎯 Example - Explicit Human Request (needs_human = TRUE):\nUser: \"بدي أحكي مع موظف مش معك\"\n{\n  \"has_appointment\": false,\n  \"is_reschedule\": false,\n  \"needs_human\": true,\n  \"appointment_date\": null,\n  \"appointment_day\": null,\n  \"appointment_time\": null,\n  \"service\": null,\n  \"lead_category\": \"ask_agent\",\n  \"description\": \"طلب صريح للتحدث مع موظف\",\n  \"notes\": \"يحتاج تدخل بشري فوري\"\n}\n\n🎯 Example - Frustration (needs_human = TRUE):\nUser: \"يا زلمة مش فاهم عليك، وديني على حدا بفهم\"\n{\n  \"has_appointment\": false,\n  \"is_reschedule\": false,\n  \"needs_human\": true,\n  \"appointment_date\": null,\n  \"appointment_day\": null,\n  \"appointment_time\": null,\n  \"service\": null,\n  \"lead_category\": \"ask_agent\",\n  \"description\": \"إحباط وطلب التحدث مع شخص\",\n  \"notes\": \"عدم رضا واضح\"\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2.2
    },
    {
      "id": "56670131-4402-4ae7-ad14-22f67064e4b6",
      "name": "Send via WhatsApp",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -80,
        48
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v22.0/{{ $('Format Context').item.json.wa_id }}/messages",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"messaging_product\": \"whatsapp\",\n  \"to\": \"{{ $('Format Context').item.json.sender }}\",\n  \"type\": \"text\",\n  \"text\":{  \"body\":{{ JSON.stringify($json.output) }}}\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "credential-id",
          "name": "whatsapp"
        }
      },
      "typeVersion": 4
    },
    {
      "id": "44b6b15f-d026-4710-8e2a-0878df5a6aa6",
      "name": "Pinecone Vector Store",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "position": [
        -1328,
        2016
      ],
      "parameters": {
        "mode": "insert",
        "options": {
          "pineconeNamespace": "1"
        },
        "pineconeIndex": {
          "__rl": true,
          "mode": "list",
          "value": "n8n",
          "cachedResultName": "n8n"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4bdb5738-0c36-40cb-ae81-315d54320894",
      "name": "Default Data Loader",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "position": [
        -1168,
        2240
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1
    },
    {
      "id": "d31374e1-7546-4dbb-8df8-50e88ae64338",
      "name": "Recursive Character Text Splitter",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "position": [
        -1184,
        2448
      ],
      "parameters": {
        "options": {},
        "chunkOverlap": 100
      },
      "typeVersion": 1
    },
    {
      "id": "1d149211-15e0-4925-aeb7-5a946fbe24ce",
      "name": "Get a document",
      "type": "n8n-nodes-base.googleDocs",
      "position": [
        -1552,
        2016
      ],
      "parameters": {
        "operation": "get",
        "documentURL": "=https://docs.google.com/document/d/1M2VXsD_gLRDbRv8VU2S2IfnCkPl5NaPqX_QulHV-050/edit?tab=t.0"
      },
      "typeVersion": 2
    },
    {
      "id": "07700abc-c2bd-44a1-8188-3d51a4022d9e",
      "name": "When clicking ‘Execute workflow’",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        -1744,
        2016
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "80e81e5e-e2f0-4bbc-80dc-02dbf0b2bca4",
      "name": "Embeddings OpenAI",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        -1504,
        2256
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "credential-id",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ffa2a0af-0af3-4466-a009-2ab4522bb72e",
      "name": "Vector Store Tool",
      "type": "@n8n/n8n-nodes-langchain.toolVectorStore",
      "position": [
        -1248,
        976
      ],
      "parameters": {
        "name": "company_documents_tool",
        "description": "Retrieve information from any company documents"
      },
      "typeVersion": 1
    },
    {
      "id": "80bd0f37-5df9-4430-adb8-253585287dbb",
      "name": "Pinecone Vector Store (Retrieval)",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "position": [
        -1344,
        1152
      ],
      "parameters": {
        "options": {},
        "pineconeIndex": {
          "__rl": true,
          "mode": "list",
          "value": "clinic",
          "cachedResultName": "clinic"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e60712a2-3949-4719-8ac1-a4b218f1f192",
      "name": "Embeddings OpenAI1",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "position": [
        -1280,
        1376
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "credential-id",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6b947bca-a0ec-486e-b483-f9e239987032",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1888,
        1952
      ],
      "parameters": {
        "color": 7,
        "width": 1024,
        "height": 656,
        "content": "## RAG System\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e78bf1f5-33f0-409a-901d-d7f8257e32ae",
      "name": "Verify • WA (hub.challenge)",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -3824,
        272
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "text/plain"
              }
            ]
          }
        },
        "respondWith": "text",
        "responseBody": "={{ $json.query['hub.challenge'] || 'OK' }}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "e13b4e56-2078-4a94-8b6a-c3e4cd1080f5",
      "name": "filter text messages",
      "type": "n8n-nodes-base.if",
      "position": [
        -3136,
        256
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.body.entry[0].changes[0].value.messages[0].type }}",
              "rightValue": "text"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f43e83f8-161b-4231-9dbc-79962676b2d5",
      "name": "If Audio",
      "type": "n8n-nodes-base.if",
      "position": [
        -3552,
        464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.body.entry[0].changes[0].value.messages[0].type }}",
              "rightValue": "audio"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "13e1d14a-d5ed-4d42-b437-ff3de78ed653",
      "name": "Get Audio",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3328,
        448
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v22.0/{{ $('WhatsApp Webhook').item.json.body.entry[0].changes[0].value.messages[0].audio.id }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "credential-id",
          "name": "whatsapp"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8e96d4bb-2210-44c6-a28d-6bcb780bf1f9",
      "name": "Download Audio",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3136,
        448
      ],
      "parameters": {
        "url": "={{$json.url}}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "credential-id",
          "name": "whatsapp"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1da24395-e5e2-4b21-8629-11a63baab66f",
      "name": "Transcribe Audio",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -2928,
        448
      ],
      "parameters": {
        "options": {
          "language": "ar",
          "temperature": 0.6
        },
        "resource": "audio",
        "operation": "transcribe"
      },
      "credentials": {
        "openAiApi": {
          "id": "credential-id",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "1ebf7b18-abb1-44cf-8638-36dca9cf28c5",
      "name": "If Image",
      "type": "n8n-nodes-base.if",
      "position": [
        -3536,
        704
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.body.entry[0].changes[0].value.messages[0].image.mime_type }}",
              "rightValue": "image"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "2105b13a-d511-4626-bc32-019d94e63749",
      "name": "If Video/PDF",
      "type": "n8n-nodes-base.if",
      "position": [
        -3520,
        880
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "1",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.body.entry[0].changes[0].value.messages[0].document.mime_type }}",
              "rightValue": "application"
            },
            {
              "id": "a584f702-7224-400b-85ff-eeff8eca35b3",
              "operator": {
                "type": "string",
                "operation": "contains"
              },
              "leftValue": "={{ $json.body.entry[0].changes[0].value.messages[0].document.mime_type }}",
              "rightValue": "video"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0d717c38-68da-47a7-a79f-7caac828e8aa",
      "name": "Get Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3296,
        688
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v22.0/{{ $('WhatsApp Webhook').item.json.body.entry[0].changes[0].value.messages[0].image.id }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "credential-id",
          "name": "whatsapp"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "1f019d11-f137-4fce-983e-8940de919d2b",
      "name": "Download Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3072,
        688
      ],
      "parameters": {
        "url": "={{$json.url}}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "credential-id",
          "name": "whatsapp"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4c2cf8bd-065a-4320-8e79-770d7cc9e4c2",
      "name": "Get file",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3296,
        864
      ],
      "parameters": {
        "url": "=https://graph.facebook.com/v22.0/{{ $('WhatsApp Webhook').item.json.body.entry[0].changes[0].value.messages[0].document.id }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "credential-id",
          "name": "whatsapp"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b6ebf3af-7914-4962-84d5-a883507b3111",
      "name": "Download file",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3088,
        864
      ],
      "parameters": {
        "url": "={{$json.url}}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "=httpBearerAuth"
      },
      "typeVersion": 4.2
    },
    {
      "id": "2d8c8b15-7149-4904-8368-b2c04423001b",
      "name": "Prepare Email With Attachment",
      "type": "n8n-nodes-base.code",
      "position": [
        -2864,
        864
      ],
      "parameters": {
        "jsCode": "// اقرأ بيانات الوِبهوك والـ input\nconst webhookData = $('WhatsApp Webhook').first().json;\nconst firstItem = $input.first();\n\nconst name = webhookData.body?.entry?.[0]?.changes?.[0]?.value?.contacts?.[0]?.profile?.name || \"\";\nconst phone = webhookData.body?.entry?.[0]?.changes?.[0]?.value?.contacts?.[0]?.wa_id || firstItem.json.phone || \"\";\n\n// لو فيه binary جاهز من عقدة سابقة، خذه\nconst bin = firstItem.binary || {};\nconst binaryKey = Object.keys(bin)[0] || \"\"; // مثال: \"file\" أو \"data\"\nif (!name || !phone || !binaryKey) {\n  throw new Error(\"مطلوب name و phone و ملف binary من العقدة السابقة.\");\n}\n\nconst subject = \"إشعار تواصل جديد\";\nconst html = `\n<!doctype html>\n<html lang=\"ar\" dir=\"rtl\">\n<head>\n  <meta charset=\"utf-8\"><title>${subject}</title>\n  <style>\n    body { background:#e8f3ff; direction:rtl; text-align:right; font-family:'Segoe UI',Roboto,Helvetica,Arial,sans-serif; margin:0; padding:0; }\n    .container { max-width:600px; margin:24px auto; background:#fff; border-radius:12px; overflow:hidden; box-shadow:0 4px 16px rgba(13,71,161,0.15); }\n    .header { background:linear-gradient(135deg,#0D47A1,#00B894); padding:20px; color:#fff; }\n    .content { padding:24px; }\n    .row { margin:0 0 12px 0; }\n    .label { color:#0D47A1; font-weight:600; }\n    .footer { background:#f7fafc; padding:16px; font-size:12px; color:#555; border-top:1px solid #e9eef8; }\n    .brand { color:#0D47A1; font-weight:600; }\n  </style>\n</head>\n<body>\n  <div class=\"container\">\n    <div class=\"header\">\n      <h2 style=\"margin:0;\">${subject}</h2>\n      <p style=\"margin:6px 0 0 0;\">لقد تم استلام طلب جديد من موقعك</p>\n    </div>\n    <div class=\"content\">\n      <p class=\"row\"><span class=\"label\">الاسم:</span> ${name}</p>\n      <p class=\"row\"><span class=\"label\">رقم الهاتف:</span> ${phone}</p>\n      <p class=\"row\">المرفق تم إرفاقه مع هذا البريد.</p>\n    </div>\n    <div class=\"footer\">\n      تم الإرسال بواسطة <span class=\"brand\">تواصل العيادات</span>\n    </div>\n  </div>\n</body>\n</html>\n`;\n\n// رجّع نفس الـ binary حتى يلتقطه Email node كمرفق\nreturn [\n  {\n    json: { subject, html },\n    binary: { [binaryKey]: bin[binaryKey] },\n  },\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f197f696-3c15-4bdf-aa64-15a87523b055",
      "name": "Send Email with attachment",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -2640,
        864
      ],
      "webhookId": "35edc50d-638d-421b-8883-aad79dd8d8a1",
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{ $json.html }}",
        "options": {
          "attachmentsUi": {
            "attachmentsBinary": [
              {}
            ]
          }
        },
        "subject": "هناك عميل أرسل ملف "
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "credential-id",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "49ed334f-d0dd-43ac-90aa-0ad08d9f6ead",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -2640,
        688
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "05294dc6-587a-4631-b03a-395ffad76a89",
              "name": "text",
              "type": "string",
              "value": "=image description:  {{ $json.content }}\nuser's message: {{ $('WhatsApp Webhook').item.json.body.entry[0].changes[0].value.messages[0].image.caption }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "4055776e-6283-472a-bfbc-374ad20cc555",
      "name": "Analyze image",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -2848,
        688
      ],
      "parameters": {
        "text": "Here is an image sent by the user. Describe the image and transcribe any text visible in the image.",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {
          "maxTokens": 50
        },
        "resource": "image",
        "inputType": "=base64",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "id": "credential-id",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "98fa0d1d-cea9-4f2f-a8dc-4cd997ebb1ce",
      "name": "Prepare Text Data",
      "type": "n8n-nodes-base.code",
      "position": [
        -2112,
        576
      ],
      "parameters": {
        "jsCode": "const items = $input.all();\n\n// Get phone number from webhook\nconst phoneNumber = $('WhatsApp Webhook').first().json.body.entry[0].changes[0].value.messages[0].from || '';\n\n// Try to get text from different sources\nlet textFromWhisper;\nlet textFromWebhook;\nlet textFromImage;\n\n// 1. Try to get transcribed voice from Whisper\ntry {\n  textFromWhisper = $('Transcribe Audio').first().json.text;\n  console.log('Voice text found:', textFromWhisper);\n} catch (e) {\n  textFromWhisper = undefined;\n}\n\n// 2. Try to get regular text message from webhook\ntry {\n  textFromWebhook = $('WhatsApp Webhook').first().json.body.entry[0].changes[0].value.messages[0].text.body;\n  console.log('Text message found:', textFromWebhook);\n} catch (e) {\n  textFromWebhook = undefined;\n}\n\n// 3. Try to get image analysis from Edit Fields node\ntry {\n  textFromImage = $('Edit Fields').first().json.text;\n  console.log('Image analysis found:', textFromImage);\n} catch (e) {\n  textFromImage = undefined;\n}\n\n// Combine all available text sources\n// Priority: voice > image > text message\nlet finalText = textFromWhisper || textFromImage || textFromWebhook || '';\n\n// Add source indicator for debugging\nlet source = '';\nif (textFromWhisper) {\n  source = 'voice';\n} else if (textFromImage) {\n  source = 'image';\n} else if (textFromWebhook) {\n  source = 'text';\n}\n\nconsole.log('Final text source:', source);\nconsole.log('Final text:', finalText);\n\nreturn items.map(item => {\n  return {\n    json: {\n      phone: phoneNumber,\n      text: finalText,\n      source: source, // للمعرفة من وين جاي النص\n      timestamp: new Date().toISOString()\n    }\n  };\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "4f37fbd4-21f2-4f31-ae58-8774cf8c21bc",
      "name": "Insert Row Memory",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -1920,
        576
      ],
      "parameters": {
        "columns": {
          "value": {
            "phone": "={{ $('WhatsApp Webhook').item.json.body.entry[0].changes[0].value.contacts[0].wa_id }}",
            "sender": "={{ $json.text }}"
          },
          "schema": [
            {
              "id": "sender",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "sender",
              "defaultMatch": false
            },
            {
              "id": "reciever",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "reciever",
              "defaultMatch": false
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "l6biIqkSE1qLJvaZ",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/l6biIqkSE1qLJvaZ",
          "cachedResultName": "test table"
        }
      },
      "executeOnce": true,
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "3ed7200b-b2d4-490c-b26d-92fbebc6c607",
      "name": "Get History",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -1728,
        576
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "phone",
              "keyValue": "={{ $('Prepare Text Data').item.json.phone }}"
            }
          ]
        },
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "l6biIqkSE1qLJvaZ",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/l6biIqkSE1qLJvaZ",
          "cachedResultName": "test table"
        }
      },
      "executeOnce": true,
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "5f0569e7-dde9-41ed-9b16-544a7b7013b8",
      "name": "Format Context",
      "type": "n8n-nodes-base.code",
      "position": [
        -1360,
        576
      ],
      "parameters": {
        "jsCode": "// استخراج البيانات الأساسية\nconst jsText = $('Prepare Text Data').first().json.text || '';\nconst phoneNumber = $('Prepare Text Data').first().json.phone || '';\nconst wa_id = $('WhatsApp Webhook').item.json.body.entry[0].changes[0].value.metadata.phone_number_id;\nconst name = $('WhatsApp Webhook').first().json.body.entry[0].changes[0].value.contacts[0].profile.name;\n\n// Get available slots\nlet availableSlots = [];\ntry {\n  const slotsData = $('Get Slots1').all();\n  console.log('Get Slots1 data:', JSON.stringify(slotsData.map(s => s.json)));\n  \n  availableSlots = slotsData.map(slot => {\n    console.log('Slot structure:', Object.keys(slot.json));\n    \n    const date = slot.json['التاريخ (A)'] || slot.json.date || slot.json.التاريخ || '';\n    const time = slot.json['الوقت (B)'] || slot.json.time || slot.json.الوقت || '';\n    const service = slot.json['الخدمة (C)'] || slot.json.service || slot.json.الخدمة || '';\n    \n    return `${date} - ${time} (${service})`;\n  });\n  \n  console.log('Available slots formatted:', availableSlots);\n} catch (e) {\n  console.error('Error fetching slots:', e.message);\n  availableSlots = [];\n}\n\n// Get existing appointments for this customer\nlet existingAppointments = [];\ntry {\n  const appointmentsData = $('Get appointment1').all();\n  console.log('Get History1 appointments:', appointmentsData.length);\n  \n  existingAppointments = appointmentsData.map(apt => {\n    console.log('Appointment structure:', Object.keys(apt.json));\n    \n    const date = apt.json.date || '';\n    const time = apt.json.times || apt.json.time || '';\n    const service = apt.json.service || '';\n    const name = apt.json.name || '';\n    \n    return `${date} at ${time} - ${service} (${name})`;\n  });\n  \n  console.log('Existing appointments formatted:', existingAppointments);\n} catch (e) {\n  console.error('Error fetching existing appointments:', e.message);\n  existingAppointments = [];\n}\n\n// Format conversation history from Get History1\nlet formattedHistory = '';\nlet historyCount = 0;\ntry {\n  const historyData = $('Get History').all().slice(0, 10);\n  historyCount = historyData.length;\n  console.log('Get History1 data:', historyCount);\n  \n  formattedHistory = historyData.map(entry => {\n    const createdAt = entry.json.createdAt || '';\n    const aiResponse = entry.json.reciever || entry.json.ai || '';\n    const userMessage = entry.json.sender || entry.json.user || '';\n    \n    // Format timestamp as yyyy-mm-dd:HH:MM\n    let formattedTime = '';\n    if (createdAt) {\n      const date = new Date(createdAt);\n      const year = date.getFullYear();\n      const month = String(date.getMonth() + 1).padStart(2, '0');\n      const day = String(date.getDate()).padStart(2, '0');\n      const hours = String(date.getHours()).padStart(2, '0');\n      const minutes = String(date.getMinutes()).padStart(2, '0');\n      formattedTime = `${year}-${month}-${day}:${hours}:${minutes}`;\n    }\n    \n    return `( ${formattedTime}, ai: ${aiResponse}, user: ${userMessage})`;\n  }).join('\\n');\n  \n  console.log('Formatted history:', formattedHistory);\n} catch (e) {\n  console.error('Error formatting history:', e.message);\n  formattedHistory = '';\n  historyCount = 0;\n}\n\n// ⚠️ FIX: Get ALL user appointments and format them\nlet userAppointments = '';\nlet userAppointmentsCount = 0;\ntry {\n  const userAppts = $input.all(); // Get ALL appointments\n  userAppointmentsCount = userAppts.length;\n  \n  if (userAppointmentsCount > 0) {\n    userAppointments = userAppts.map((apt, index) => {\n      const aptName = apt.json.name || '';\n      const aptService = apt.json.service || '';\n      const aptDate = apt.json.date ? apt.json.date.split('T')[0] : '';\n      const aptDay = apt.json.day || '';\n      const aptTime = apt.json.times || '';\n      \n      return `\nموعد ${index + 1}:\nالاسم: ${aptName}\nالخدمة: ${aptService}\n📅 التاريخ: ${aptDate} | ${aptDay}\n🕐 الوقت: ${aptTime}`;\n    }).join('\\n---\\n');\n  } else {\n    userAppointments = 'لا يوجد مواعيد محجوزة حالياً';\n  }\n  \n  console.log('User appointments formatted:', userAppointments);\n} catch (e) {\n  console.error('Error fetching user appointments:', e.message);\n  userAppointments = 'لا يوجد مواعيد محجوزة حالياً';\n  userAppointmentsCount = 0;\n}\nlet type = $('Prepare Text Data').first().json.source;\nlet users_id = $('Insert Row Memory').first().json.id;\n\nconst result = {\n  sender: phoneNumber,\n  text: jsText,\n  available_slots: availableSlots.join('\\n'), \n  available_slots_count: availableSlots.length,\n  existing_appointments: existingAppointments.join('\\n'),\n  existing_appointments_count: existingAppointments.length,\n  formatted_output: formattedHistory,\n  history_count: historyCount,\n  name,\n  wa_id,\n  users_appointments: userAppointments, // ✅ All appointments formatted\n  users_appointments_count: userAppointmentsCount, // ✅ Count of appointments\n  users_id,\n  type\n};\n\nconsole.log('Final output:', JSON.stringify(result, null, 2));\n\nreturn [\n  {\n    json: result\n  }\n];\n"
      },
      "executeOnce": true,
      "typeVersion": 2,
      "alwaysOutputData": true
    },
    {
      "id": "ac272c74-a855-4a88-ae47-71b12ef39ab2",
      "name": "Main AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1120,
        576
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "options": {
          "systemMessage": "=## Medical Clinic Bot\nToday: {{ $now }}\nUser: {{ $json.name }} ({{ $json.sender }})\n\n## Knowledge source:\n\n- Use the **Pinecone vector store** to answer anything about the clinic (services, policies, offers, location, doctors, instructions, etc.).\n-Always use this Memory/last conversations:\n{{ $json.formatted_output }}\n----\nSlots:\n{{ $json.available_slots }}\n-----\nAppointments:\n{{ $json.users_appointments }}\n-----\n\nRules: Arabic only, no markdown, verify data, 2-digit time (12=twelve), different services for multiple bookings\nImportant Rule:\n--- you do not invent dates, think twice before makeing up a date make sure it is correct \n\n-تقدم ثلاث خدمات, feller, عناية بالبشرة, تبييض أسنان\n\nBooking: تم حجز موعدك! ✅ 📅 [date]|[day] 🕐 [time] 💉 [service]\nReschedule: تم تعديل موعدك 📅 [date]|[day] 🕐 [time] 💉 [service]\n\n## IMPORTANT: if image is equal to  {{ $json.type }} then answer with a short answer of the image and user message if existed"
        },
        "promptType": "=define",
        "hasOutputParser": true
      },
      "executeOnce": true,
      "typeVersion": 2.2
    },
    {
      "id": "7ff93428-5292-47ec-8084-c56bd2ccd6ec",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        -1264,
        784
      ],
      "parameters": {
        "sessionKey": "={{ $json.sender }}",
        "sessionIdType": "customKey",
        "contextWindowLength": 1
      },
      "typeVersion": 1.3
    },
    {
      "id": "bc33c7ae-22a5-417f-bc02-e4e59cc96eb6",
      "name": "save appointment - different service",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1024,
        320
      ],
      "parameters": {
        "columns": {
          "value": {
            "day": "={{ $json.appointment_day }}",
            "date": "={{ $json.appointment_date }}",
            "name": "={{ $('Output Format Text').item.json.customer_name }}",
            "phone": "={{ $json.phone }}",
            "times": "={{ $json.appointment_time }}",
            "service": "={{ $json.service }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "name",
              "defaultMatch": false
            },
            {
              "id": "phone",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false
            },
            {
              "id": "date",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "date",
              "defaultMatch": false
            },
            {
              "id": "day",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "day",
              "defaultMatch": false
            },
            {
              "id": "service",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "service",
              "defaultMatch": false
            },
            {
              "id": "times",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "times",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "XICUlFpXl76eV2Dm",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/XICUlFpXl76eV2Dm",
          "cachedResultName": "appointments"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "96690897-ea79-4060-b288-f532b516a0af",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4688,
        96
      ],
      "parameters": {
        "width": 480,
        "height": 704,
        "content": "## 🏥🤖 Medical Clinic WhatsApp Agent + CRM Sync\n\nThis workflow receives WhatsApp Cloud API messages via a Webhook and processes them based on message type (📝 Text / 🎙️ Audio / 🖼️ Image / 📎 Document).  \nFor audio messages 🎙️, it downloads the media and transcribes it into Arabic text (Whisper).  \nFor images 🖼️, it downloads the image and analyzes it to describe the content and extract any visible text (Vision).  \nFor documents/PDFs 📎, it downloads the file and sends a Gmail notification ✉️ to staff with the attachment.\n\nAfter normalizing the final customer text (🎙️ voice > 🖼️ image > 📝 text), the workflow builds a structured context 🧠 that includes customer data 👤, recent conversation history 💬 (memory), the customer’s upcoming appointments 📅, and available booking slots 🕐. The message is then sent to the Main AI Agent 🤖 (Arabic only 🇸🇦/🇯🇴), with RAG enabled 📚 using Pinecone 🌲 to answer clinic-related questions from uploaded clinic documents (Google Docs).\n\nThe AI response is sent back to the customer via WhatsApp ✅. In parallel, a strict JSON extractor 🧾 parses key fields (booking/reschedule details, service 💉, date/time ⏰, lead category 🧲, and human escalation 🧑‍⚕️). If `needs_human` is true 🚨, the workflow emails staff ✉️ and stores the case in a “human” queue table 📥.  \nA scheduled sync ⏲️ (every 1 minute) updates a Google Sheets CRM 📊 from n8n Data Tables (slots/leads/human/appointments).\n\n**Requirements:** WhatsApp Cloud API token 🔑 + phone_number_id 📱, Gmail ✉️ + Google Sheets 📊 credentials, Pinecone 🌲 index/namespace, and the referenced n8n Data Tables 🗂️.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e5d403fa-47d3-4d8f-a4f5-96c7feb21782",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4016,
        48
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 208,
        "content": "## Section 1 — Webhook & Verification\nReceives WhatsApp webhooks, responds to `hub.challenge` verification, and routes incoming messages by type.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ad430754-6c6a-4838-ad0e-52b3d367977d",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -368,
        -256
      ],
      "parameters": {
        "color": 7,
        "width": 1552,
        "height": 752,
        "content": "## Section 5 — Extraction + Appointment Save\n\n- **AI Data Extractor**: reads the agent output and extracts strict JSON fields (date/day/time/service, booking vs reschedule, needs_human, lead_category).\n- **Code in JavaScript1**: parses the JSON safely, enriches it with customer metadata (phone/name/timestamps), and normalizes YES/NO flags.\n- **Check Appointment1**: routes only when `has_appointment = true`.\n- **save appointment - same service (upsert)**: updates/creates an appointment row matched by `(phone + service)` so each service has its own record.\n- **If Appointment is ready**: compares the new service vs previous/checked service; if different, allows creating another booking record.\n- **save appointment - different service (insert)**: inserts a new appointment row for an additional service (separate from existing one).\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "82315f3e-7edb-4ece-a709-2dc2b12782ac",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -384,
        736
      ],
      "parameters": {
        "color": 7,
        "width": 1568,
        "height": 736,
        "content": "## Section 6 — Leads + Human Escalation + Logging\n\n- **Get leads**: fetches existing lead rows for this phone to prevent duplicates.\n- **Check if Lead**: decides if a new lead should be created (based on existence/conditions).\n- **AI Summarizer1**: produces compact JSON `{summary, category}` for the customer message.\n- **Format output**: parses/cleans the summarizer JSON and outputs `summary` + `category`.\n- **Insert Lead**: stores the lead with `short_sum`, `lead_category`, and customer identity.\n- **Check Human is called**: routes only when `needs_human = true`.\n- **Send Email Notification If Human is called**: emails staff with the customer details (HTML template).\n- **Add row for Human Call (upsert)**: upserts the request into the “human” queue table keyed by phone.\n- **Update Row**: writes the final AI reply back into the conversation/memory table row (stores receiver text).\n- **Send via WhatsApp**: sends the final reply to the customer through WhatsApp Cloud A"
      },
      "typeVersion": 1
    },
    {
      "id": "378909c2-d5e4-42eb-9272-07885946c4c3",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2336,
        320
      ],
      "parameters": {
        "color": 7,
        "width": 1152,
        "height": 400,
        "content": "## Section 3 — Memory & Context Builder\n\n- **Prepare Text Data (Code):** merges message sources into `finalText` (voice → image → text) + adds `phone`, `source`, `timestamp`.\n- **Insert Row Memory (Data Table):** saves the incoming message (`phone`, `sender = finalText`) to the memory table.\n- **Get History (Data Table):** pulls recent messages for the same `phone` for short-term context.\n- **Get only users appointment (Data Table):** fetches upcoming appointments for that `phone` (`date >= now`).\n- **Format Context (Code):** builds one AI-ready JSON (last 10 messages + upcoming appointments + `name`, `wa_id`, `users_id`, `type`).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "323bf31d-8d4d-486a-b5b9-0e2934be1f2c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "disabled": true,
      "position": [
        -3888,
        1456
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 240,
        "content": "## Section 7 — CRM Sync to Google Sheets\nScheduled job that syncs n8n Data Tables (slots/leads/human/appointments) into a Google Sheets CRM every minute.\n\nFollow this like for the google sheets sheet: https://docs.google.com/spreadsheets/d/1HCl3CvMnzILIrcjnFK-AwLqWuKfMwezM1yXWkJ4KG9Q/edit?usp=sharing\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "bd14901c-b388-48b7-9d23-229494a98d78",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3584,
        -96
      ],
      "parameters": {
        "color": 6,
        "width": 1088,
        "height": 1152,
        "content": "## Section 2 — Media Handling (Audio / Image / File)\nFetches media metadata from Graph API, downloads files, transcribes audio, analyzes images, and emails staff when a document/PDF is received.\n\n- Detects message type and routes:\n  - 📝 Text → Normalize text\n  - 🎙️ Audio → Download + Transcribe\n  - 🖼️ Image → Download + Analyze\n  - 📎 Document/PDF → Download + Email staff\n\n**Nodes:** `filter text messages`, `If Audio`, `If Image`, `If Video/PDF`\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fe6c6619-55d4-4ad5-8eb1-2f6bfb1816da",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -848,
        1952
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 192,
        "content": "## Section 8 — RAG Knowledge Base (Manual Run)\nLoads a Google Doc, splits it into chunks, generates embeddings, and inserts them into Pinecone for retrieval (RAG).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "d4396a87-8377-4f3f-9628-791ed648e6f4",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1952,
        800
      ],
      "parameters": {
        "color": 6,
        "width": 624,
        "height": 416,
        "content": "## Section 4 — AI Agent + Tools (RAG + Appointments + Memory)\n\n- **OpenAI Chat Model**: the LLM that powers the agent responses.\n- **Main AI Agent**: orchestrates the conversation, enforces rules, and decides when to call tools.\n- **Simple Memory**: keeps a tiny per-user context window (`sender` as session key).\n- **Tools**\n  - **Vector Store Tool**: retrieves answers from stored documents (RAG).\n  - **Booked Appointment**: reads appointments from the Data Table for booking/reschedule checks.\n- **Embeddings OpenAI**: embeddings configuration used by the vector retrieval layer (when enabled).\n"
      },
      "typeVersion": 1
    },
    {
      "id": "da9b02bf-731c-40aa-a518-d3cef220f116",
      "name": "WhatsApp Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -4096,
        528
      ],
      "webhookId": "clinic-bot-webhook",
      "parameters": {
        "path": "whatsapp",
        "options": {},
        "responseMode": "responseNode",
        "multipleMethods": true
      },
      "typeVersion": 2.1
    },
    {
      "id": "c2e8fd84-9286-4f9e-8387-910e400f96d7",
      "name": "Get only users appointment",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -1520,
        576
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "date",
              "keyValue": "={{ $now.toISO() }}",
              "condition": "gte"
            },
            {
              "keyName": "phone",
              "keyValue": "={{ $json.phone }}"
            }
          ]
        },
        "matchType": "allConditions",
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "XICUlFpXl76eV2Dm",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/XICUlFpXl76eV2Dm",
          "cachedResultName": "appointments"
        }
      },
      "executeOnce": true,
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "a436cab9-3496-4c75-8943-2b48df24c9a9",
      "name": "Get leads",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -320,
        1280
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "phone",
              "keyValue": "={{ $('Format Context').item.json.sender }}"
            }
          ]
        },
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "16bAoJicIDEPak46",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/16bAoJicIDEPak46",
          "cachedResultName": "leads"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "75465bfb-f042-4d82-bf84-a4b316188926",
      "name": "Send Email Notification If Human is called",
      "type": "n8n-nodes-base.gmail",
      "position": [
        176,
        1072
      ],
      "webhookId": "35edc50d-638d-421b-8883-aad79dd8d8a1",
      "parameters": {
        "sendTo": "user@example.com",
        "message": "=<!doctype html>\n<html lang=\"ar\" dir=\"rtl\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"x-apple-disable-message-reformatting\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <title>إشعار جديد</title>\n  <style>\n    body, table, td, p { margin:0; padding:0; }\n    img { border:0; line-height:100%; outline:none; text-decoration:none; }\n    table { border-collapse:collapse; }\n    body { background:#e8f3ff; direction:rtl; text-align:right; font-family:'Segoe UI',Roboto,Helvetica,Arial,sans-serif; }\n    .wrap { width:100%; background:#e8f3ff; }\n    .container { width:100%; max-width:600px; background:#ffffff; border-radius:16px; overflow:hidden; box-shadow:0 6px 24px rgba(13,71,161,0.15); text-align:right; }\n    .p-32 { padding:32px 12px; }\n    .p-card { padding:28px; }\n    .header { background:linear-gradient(135deg,#0D47A1 0%,#00B894 100%); padding:24px; text-align:right; }\n    .h1 { margin:0; font-size:22px; line-height:1.3; color:#ffffff; text-align:right; }\n    .muted { margin:6px 0 0 0; font-size:13px; color:#e3f7ff; opacity:0.9; text-align:right; }\n    .text { font-size:15px; line-height:1.6; color:#0f172a; text-align:right; }\n    .small { font-size:12px; color:#64748b; text-align:right; }\n    .info { width:100%; border-collapse:separate; border-spacing:0 10px; }\n    .key { width:160px; padding:12px 14px; font-size:13px; font-weight:600; border-radius:0 12px 12px 0; border:1px solid; border-left:0; vertical-align:top; background:#f1f8ff; text-align:right; }\n    .val { padding:12px 14px; font-size:14px; color:#0f172a; background:#fff; border-radius:12px 0 0 12px; border:1px solid; border-right:0; vertical-align:top; text-align:right; }\n    .key-blue { background:#f1f8ff; color:#0D47A1; border-color:#d7e7ff; }\n    .val-blue { border-color:#d7e7ff; }\n    .key-green { background:#eefcf6; color:#007f67; border-color:#ccefe2; }\n    .val-green { border-color:#ccefe2; }\n    .desc { padding:14px; line-height:1.7; text-align:right; }\n    .hr { border:none; border-top:1px solid #e6eefc; margin:26px 0; }\n    .btn { display:inline-block; padding:12px 18px; font-size:14px; color:#ffffff; text-decoration:none; background:#00B894; border-radius:10px; text-align:center; }\n    .foot { background:#f7fafc; border-top:1px solid #e9eef8; padding:16px 24px; text-align:right; }\n    .brand { color:#0D47A1; font-weight:600; }\n    @media (prefers-color-scheme: dark) {\n      body { background:#0b1220; }\n      .wrap { background:#0b1220; }\n      .container { background:#0f172a; color:#e5e7eb; }\n      .text { color:#e5e7eb; }\n      .small { color:#94a3b8; }\n    }\n  </style>\n</head>\n<body>\n  <div style=\"display:none;max-height:0;overflow:hidden;opacity:0;color:transparent;\">\n    إشعار جديد من نموذج موقعك، يحتوي على الاسم، الهاتف، والوصف.\n  </div>\n\n  <table role=\"presentation\" class=\"wrap\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n    <tr>\n      <td align=\"center\" class=\"p-32\">\n        <table role=\"presentation\" class=\"container\" width=\"600\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\">\n          <tr>\n            <td class=\"header\">\n              <h1 class=\"h1\">إشعار تواصل جديد</h1>\n              <p class=\"muted\">لقد استقبلت رسالة جديدة من نموذج موقعك</p>\n            </td>\n          </tr>\n\n          <tr>\n            <td class=\"p-card\">\n              <p class=\"text\">مرحباً، تم إرسال طلب جديد، التفاصيل أدناه:</p>\n\n              <table role=\"presentation\" class=\"info\">\n                <tr>\n\n                  <td class=\"val val-blue\">{{ $json.customer_name }}</td>\n                  <td class=\"key key-blue\">الاسم</td>\n                </tr>\n                <tr>\n                  \n                  <td class=\"val val-green\">\n                    <a style=\"color:#0D47A1; text-decoration:none; border-bottom:1px dotted #0D47A1;\">\n                      {{ $json.phone }}\n                    </a>\n                  </td>\n<td class=\"key key-green\">رقم الهاتف</td>\n                </tr>\n                <tr>\n\n                  <td class=\"val val-blue desc\">{{ $json.description }}</td>\n                  <td class=\"key key-blue\">الوصف</td>\n\n                </tr>\n\n              </table>\n\n              <hr class=\"hr\">\n\n              <a class=\"btn\" href=\"{{VIEW_LINK}}\">عرض في لوحة التحكم</a>\n\n              <p class=\"small\" style=\"margin-top:18px;\">إذا لم تكن تتوقع هذه الرسالة، يمكنك تجاهلها بأمان.</p>\n            </td>\n          </tr>\n\n          <tr>\n            <td class=\"foot\">\n              <table role=\"presentation\" width=\"100%\">\n                <tr>\n                  <td class=\"small\">تم الإرسال بواسطة <span class=\"brand\">تواصل العيادات</span></td>\n                  <td align=\"left\">\n                    <a href=\"{{UNSUBSCRIBE_LINK}}\" class=\"small\" style=\"color:#00B894; text-decoration:none;\">إلغاء الاشتراك</a>\n                  </td>\n                </tr>\n              </table>\n            </td>\n          </tr>\n\n        </table>\n      </td>\n    </tr>\n  </table>\n</body>\n</html>\n",
        "options": {},
        "subject": "هناك عميل يحتااج تواصل بشري "
      },
      "credentials": {
        "gmailOAuth2": {
          "id": "credential-id",
          "name": "Gmail account"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "fd2b5e7e-fed0-4157-94c4-f2c92fe324f5",
      "name": "Add row for Human Call",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        384,
        1072
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $('Output Format Text').item.json.customer_name }}",
            "phone": "={{ $('Output Format Text').item.json.phone }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "name",
              "defaultMatch": false
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false
            },
            {
              "id": "descreption",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "descreption",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "phone",
              "keyValue": "={{ $('Output Format Text').item.json.phone }}"
            }
          ]
        },
        "options": {},
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "ygicufEiNou6tVCb",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/ygicufEiNou6tVCb",
          "cachedResultName": "human"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f7b05031-28df-4e57-8974-a79adbe55168",
      "name": "If Appointment is ready",
      "type": "n8n-nodes-base.if",
      "position": [
        752,
        336
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2266b20d-675a-4443-87d0-04e550575d2f",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "leftValue": "={{ $('Output Format Text').item.json.service }}",
              "rightValue": "={{ $('Check Appointment1').item.json.service }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a7599be1-1a29-4fe8-b051-adb3b4792450",
      "name": "save appointment - same service",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        592,
        336
      ],
      "parameters": {
        "columns": {
          "value": {
            "day": "={{ $json.appointment_day }}",
            "date": "={{ $json.appointment_date }}",
            "name": "={{ $('Output Format Text').item.json.customer_name }}",
            "phone": "={{ $json.phone }}",
            "times": "={{ $json.appointment_time }}",
            "service": "={{ $json.service }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "name",
              "defaultMatch": false
            },
            {
              "id": "phone",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false
            },
            {
              "id": "date",
              "type": "dateTime",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "date",
              "defaultMatch": false
            },
            {
              "id": "day",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "day",
              "defaultMatch": false
            },
            {
              "id": "service",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "service",
              "defaultMatch": false
            },
            {
              "id": "times",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "times",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "phone",
              "keyValue": "={{ $json.phone }}"
            },
            {
              "keyName": "service",
              "keyValue": "={{ $json.service }}"
            }
          ]
        },
        "options": {},
        "matchType": "allConditions",
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "XICUlFpXl76eV2Dm",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/XICUlFpXl76eV2Dm",
          "cachedResultName": "appointments"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "3de13584-3543-4ffc-9d3d-8a07bb9c0180",
      "name": "Check Human is called",
      "type": "n8n-nodes-base.if",
      "position": [
        -48,
        1088
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "7119e222-a2a0-4fdb-930f-eb16877c448e",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.needs_human }}",
              "rightValue": "YES"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a903f878-9619-4f5e-8b6a-cd2d5cd6dd81",
      "name": "Check if Lead",
      "type": "n8n-nodes-base.if",
      "position": [
        -112,
        1280
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ebc66b41-b8e2-4dc8-83e9-12f3e7593c6d",
              "operator": {
                "type": "string",
                "operation": "notExists",
                "singleValue": true
              },
              "leftValue": "={{ $json.name }}",
              "rightValue": "={{ $('Format Context').item.json.sender.toNumber() }}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8b4f5ac0-4358-47df-a009-7f7c14c219a0",
      "name": "Insert Lead",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        624,
        1264
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $('Format Context').item.json.name }}",
            "phone": "={{ $('Format Context').item.json.sender }}",
            "short_sum": "={{ $json.summary }}",
            "lead_category": "={{ $json.category }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "name",
              "defaultMatch": false
            },
            {
              "id": "phone",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false
            },
            {
              "id": "lead_category",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "lead_category",
              "defaultMatch": false
            },
            {
              "id": "short_sum",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "short_sum",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "16bAoJicIDEPak46",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/16bAoJicIDEPak46",
          "cachedResultName": "leads"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "314743b6-dd00-46e6-b7bc-25f5f994312b",
      "name": "Update Row",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -80,
        192
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [
            {
              "id": "sender",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "sender",
              "defaultMatch": false
            },
            {
              "id": "reciever",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "reciever",
              "defaultMatch": false
            },
            {
              "id": "phone",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "phone",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyValue": "={{ $('Format Context').item.json.users_id }}"
            }
          ]
        },
        "options": {},
        "operation": "upsert",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "l6biIqkSE1qLJvaZ",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/l6biIqkSE1qLJvaZ",
          "cachedResultName": "test table"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "d514e95e-b150-4291-8a6c-f93abfcb871a",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -704,
        1232
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "credential-id",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2c70c555-a008-4e5b-a955-5ddd78ffd054",
      "name": "Output Format Text",
      "type": "n8n-nodes-base.code",
      "position": [
        80,
        352
      ],
      "parameters": {
        "jsCode": "// Parse JSON from AI Data Extractor and add metadata\nconst raw = $input.first().json.output || '{}';\nconst phone = $('Format Context').first().json.sender || '';\nconst name = $('Format Context').first().json.name || '';\n\nlet data;\n\ntry {\n  // Try parsing as-is first\n  data = typeof raw === 'object' ? raw : JSON.parse(raw);\n} catch (e) {\n  // Clean and retry if initial parse fails\n  try {\n    let cleaned = raw\n      .replace(/```json\\n?/g, '')\n      .replace(/```\\n?/g, '')\n      .trim();\n    \n    // Extract JSON object\n    const jsonMatch = cleaned.match(/\\{[\\s\\S]*\\}/);\n    cleaned = jsonMatch ? jsonMatch[0] : cleaned;\n    \n    data = JSON.parse(cleaned);\n  } catch (e2) {\n    // Fallback to default structure\n    console.error('JSON parse failed:', e2.message);\n    console.error('Raw input:', raw);\n    \n    data = {\n      has_appointment: false,\n      is_reschedule: false,\n      needs_human: false,\n      appointment_date: null,\n      appointment_day: null,\n      appointment_time: null,\n      service: null,\n      lead_category: 'other',\n      description: 'فشل في استخراج البيانات',\n      notes: 'خطأ في تحليل الرد'\n    };\n  }\n}\n\n// Enrich with metadata\nconst enrichedData = {\n  ...data,\n  phone,\n  customer_name: name,\n  created_at: new Date().toISOString(),\n  has_appointment_text: data.has_appointment ? 'YES' : 'NO',\n  is_reschedule_text: data.is_reschedule ? 'YES' : 'NO',\n  needs_human_text: data.needs_human ? 'YES' : 'NO'\n};\n\n// Replace null/undefined with empty strings\nObject.keys(enrichedData).forEach(key => {\n  if (enrichedData[key] === null || enrichedData[key] === undefined) {\n    enrichedData[key] = '';\n  }\n});\n\nconsole.log('Enriched data:', JSON.stringify(enrichedData, null, 2));\n\nreturn [{ json: enrichedData }];"
      },
      "typeVersion": 2
    },
    {
      "id": "06ff90e2-b5bd-46aa-9d31-78bed0257dba",
      "name": "Format output",
      "type": "n8n-nodes-base.code",
      "position": [
        432,
        1264
      ],
      "parameters": {
        "jsCode": "// Split into two separate message outputs\nconst input = $input.first().json;\nlet response = input.output || input.text || input.response || input;\n\nconsole.log('Raw input:', JSON.stringify(input, null, 2));\n\ntry {\n  if (typeof response === 'object' && response !== null && (response.summary || response.category)) {\n    // Return as single output with both fields\n    return {\n      json: {\n        summary: response.summary || \"\",\n        category: response.category || \"\"\n      }\n    };\n  }\n  \n  if (typeof response === 'string') {\n    response = response.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n    const data = JSON.parse(response);\n    \n    return {\n      json: {\n        summary: data.summary || \"\",\n        category: data.category || \"\"\n      }\n    };\n  }\n  \n  throw new Error('Unexpected response format');\n  \n} catch (error) {\n  console.error('Parse error:', error.message);\n  return {\n    json: {\n      summary: \"Error parsing response\",\n      category: \"error\"\n    }\n  };\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "505865d1-a9bd-40a9-90bb-246de0e44704",
      "name": "Get slots",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -2704,
        1696
      ],
      "parameters": {
        "operation": "get",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "DySLjqEPX2i5RBdj",
          "cachedResultUrl": "/projects/0WLzulivX2ChGmk8/datatables/DySLjqEPX2i5RBdj",
          "cachedResultName": "slots"
        }
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "Get file": {
      "main": [
        [
          {
            "node": "Download file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get lead": {
      "main": [
        [
          {
            "node": "Append or update lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Audio": {
      "main": [
        [
          {
            "node": "Get Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Image": {
      "main": [
        [
          {
            "node": "Get Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Audio": {
      "main": [
        [
          {
            "node": "Download Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Image": {
      "main": [
        [
          {
            "node": "Download Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get leads": {
      "main": [
        [
          {
            "node": "Check if Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get slots": {
      "main": [
        [
          {
            "node": "Append or update slot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Prepare Text Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get History": {
      "main": [
        [
          {
            "node": "Get only users appointment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Video/PDF": {
      "main": [
        [
          {
            "node": "Get file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze image": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check if Lead": {
      "main": [
        [
          {
            "node": "AI Summarizer1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download file": {
      "main": [
        [
          {
            "node": "Prepare Email With Attachment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format output": {
      "main": [
        [
          {
            "node": "Insert Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Main AI Agent": {
      "main": [
        [
          {
            "node": "Send via WhatsApp",
            "type": "main",
            "index": 0
          },
          {
            "node": "Update Row",
            "type": "main",
            "index": 0
          },
          {
            "node": "AI Data Extractor",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get leads",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "Main AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "AI Summarizer1": {
      "main": [
        [
          {
            "node": "Format output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Audio": {
      "main": [
        [
          {
            "node": "Transcribe Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Image": {
      "main": [
        [
          {
            "node": "Analyze image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Context": {
      "main": [
        [
          {
            "node": "Main AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a document": {
      "main": [
        [
          {
            "node": "Pinecone Vector Store",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get human call": {
      "main": [
        [
          {
            "node": "Append or update human call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get slots",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get lead",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get human call",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get human appointment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe Audio": {
      "main": [
        [
          {
            "node": "Prepare Text Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WhatsApp Webhook": {
      "main": [
        [
          {
            "node": "Verify • WA (hub.challenge)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "If Audio",
            "type": "main",
            "index": 0
          },
          {
            "node": "filter text messages",
            "type": "main",
            "index": 0
          },
          {
            "node": "If Image",
            "type": "main",
            "index": 0
          },
          {
            "node": "If Video/PDF",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Data Extractor": {
      "main": [
        [
          {
            "node": "Output Format Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings OpenAI": {
      "ai_embedding": [
        [
          {
            "node": "Pinecone Vector Store",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Insert Row Memory": {
      "main": [
        [
          {
            "node": "Get History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Summarizer1",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "AI Data Extractor",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Main AI Agent",
            "type": "ai_languageModel",
            "index": 0
          },
          {
            "node": "Vector Store Tool",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Text Data": {
      "main": [
        [
          {
            "node": "Insert Row Memory",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Vector Store Tool": {
      "ai_tool": [
        [
          {
            "node": "Main AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Booked Appointment": {
      "ai_tool": [
        [
          {
            "node": "Main AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Check Appointment1": {
      "main": [
        [
          {
            "node": "save appointment - same service",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings OpenAI1": {
      "ai_embedding": [
        [
          {
            "node": "Pinecone Vector Store (Retrieval)",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Output Format Text": {
      "main": [
        [
          {
            "node": "Check Appointment1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Check Human is called",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Default Data Loader": {
      "ai_document": [
        [
          {
            "node": "Pinecone Vector Store",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "filter text messages": {
      "main": [
        [
          {
            "node": "Prepare Text Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check Human is called": {
      "main": [
        [
          {
            "node": "Send Email Notification If Human is called",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get human appointment": {
      "main": [
        [
          {
            "node": "Append or update appointment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Appointment is ready": {
      "main": [
        [
          {
            "node": "save appointment - different service",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get only users appointment": {
      "main": [
        [
          {
            "node": "Format Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Email With Attachment": {
      "main": [
        [
          {
            "node": "Send Email with attachment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "save appointment - same service": {
      "main": [
        [
          {
            "node": "If Appointment is ready",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pinecone Vector Store (Retrieval)": {
      "ai_vectorStore": [
        [
          {
            "node": "Vector Store Tool",
            "type": "ai_vectorStore",
            "index": 0
          }
        ]
      ]
    },
    "Recursive Character Text Splitter": {
      "ai_textSplitter": [
        [
          {
            "node": "Default Data Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "Get a document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Email Notification If Human is called": {
      "main": [
        [
          {
            "node": "Add row for Human Call",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}