{
  "name": "Conversation Orchestrator",
  "nodes": [
    {
      "parameters": {},
      "id": "1",
      "name": "Execute Workflow Trigger",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1,
      "position": [160, 420]
    },
    {
      "parameters": {
        "jsCode": "const input = $json;\nconst existingConversationId = input.conversation_id;\nconst conversationId = existingConversationId || `conv_${input.channel}_${input.user_id}_${Date.now()}`;\nconst text = (input.message_text || '').trim();\nconst normalizedText = text.toLocaleLowerCase('tr-TR');\nconst phoneRegex = /(?:\\+?90|0)?\\s*5\\d{2}\\s*\\d{3}\\s*\\d{2}\\s*\\d{2}/;\nconst phoneMatch = (input.phone || text).match(phoneRegex);\nconst phone = phoneMatch ? phoneMatch[0].replace(/\\D/g, '') : (input.phone || '');\nconst wantsHuman = /insan|temsilci|yetkili|canli destek/.test(normalizedText);\nconst wantsCallback = /arayin|beni arayin|bana ulasin|telefon/.test(normalizedText);\nconst wantsAppointment = /randevu|gorusme|uygun saat|musait/.test(normalizedText);\nconst asksPrice = /fiyat|ucret|ne kadar|paket/.test(normalizedText);\nlet intent = 'general_info';\nif (wantsHuman) intent = 'human_handoff';\nelse if (wantsAppointment) intent = 'appointment_request';\nelse if (wantsCallback) intent = 'callback_request';\nelse if (asksPrice) intent = 'pricing';\nreturn [{ json: {\n  ...input,\n  conversation_id: conversationId,\n  message_text: text,\n  normalized_text: normalizedText,\n  phone,\n  intent,\n  callback_request: wantsCallback,\n  wants_appointment: wantsAppointment,\n  wants_human: wantsHuman,\n  appointment_status: 'none',\n  lead_status: 'new',\n  language: 'tr',\n  dedupe_key: input.dedupe_key || `${input.channel}:${input.user_id}:${text}`,\n  created_at: new Date().toISOString(),\n  updated_at: new Date().toISOString()\n}}];"
      },
      "id": "2",
      "name": "Prepare Conversation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [420, 420]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": "={{$env.GOOGLE_SHEETS_LEADS_SPREADSHEET_ID}}",
        "sheetName": "leads",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "conversation_id": "={{$json.conversation_id}}",
            "channel": "={{$json.channel}}",
            "user_id": "={{$json.user_id}}",
            "name": "={{$json.name}}",
            "phone": "={{$json.phone}}",
            "product_interest": "={{$json.product_interest || ''}}",
            "intent": "={{$json.intent}}",
            "callback_request": "={{$json.callback_request}}",
            "appointment_status": "={{$json.appointment_status}}",
            "summary": "",
            "raw_message": "={{$json.message_text}}",
            "created_at": "={{$json.created_at}}",
            "updated_at": "={{$json.updated_at}}"
          }
        },
        "options": {}
      },
      "id": "3",
      "name": "Log Lead Row",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [700, 240],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "Google Sheets"
        }
      }
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": "={{$env.GOOGLE_SHEETS_KB_SPREADSHEET_ID}}",
        "sheetName": "knowledge_base",
        "options": {}
      },
      "id": "4",
      "name": "Load Knowledge Base",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [700, 580],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "GOOGLE_SHEETS_CREDENTIAL_ID",
          "name": "Google Sheets"
        }
      }
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineAll",
        "options": {}
      },
      "id": "5",
      "name": "Merge Context",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [960, 420]
    },
    {
      "parameters": {
        "jsCode": "const trigger = $input.all()[0]?.json || {};\nconst kbRows = $input.all().slice(1).map(item => item.json);\nconst normalizedText = trigger.normalized_text || '';\nconst scored = kbRows.map(row => {\n  const haystack = `${row.question || ''} ${(row.tags || '')}`.toLocaleLowerCase('tr-TR');\n  let score = 0;\n  for (const token of normalizedText.split(/\\s+/).filter(Boolean)) {\n    if (haystack.includes(token)) score += 1;\n  }\n  return { row, score };\n}).sort((a, b) => b.score - a.score);\nconst best = scored[0]?.score > 0 ? scored[0].row : null;\nreturn [{ json: {\n  ...trigger,\n  kb_answer: best?.answer || '',\n  kb_cta: best?.cta || '',\n  kb_found: !!best,\n  kb_category: best?.category || ''\n}}];"
      },
      "id": "6",
      "name": "Match Knowledge",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1210, 420]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.wants_human}}",
              "operation": "true"
            }
          ]
        }
      },
      "id": "7",
      "name": "Needs Human?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [1460, 420]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.kb_found}}",
              "operation": "true"
            }
          ]
        }
      },
      "id": "8",
      "name": "Knowledge Found?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [1710, 560]
    },
    {
      "parameters": {
        "url": "https://api.openai.com/v1/responses",
        "authentication": "none",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env.OPENAI_API_KEY}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "gpt-4.1-mini"
            },
            {
              "name": "input",
              "value": "={{[\n  {\n    role: 'system',\n    content: [\n      {\n        type: 'input_text',\n        text: 'Sen Turkce konusan bir satis asistani botsun. Sadece verilen urun bilgilerini kullan. Bilmedigin bir bilgiyi uydurma. Gerekirse telefon numarasi iste, geri arama oner veya randevu slotu sun. Cok kisa, net ve profesyonel yaz.'\n      }\n    ]\n  },\n  {\n    role: 'user',\n    content: [\n      {\n        type: 'input_text',\n        text: `Kullanici mesaji: ${$json.message_text}\\nIntent: ${$json.intent}\\nBilgi tabani cevabi: ${$json.kb_answer || 'YOK'}\\nCTA: ${$json.kb_cta || ''}\\nTelefon: ${$json.phone || 'YOK'}\\nInsan devri gerekir mi: ${$json.wants_human ? 'EVET' : 'HAYIR'}`\n      }\n    ]\n  }\n]}}"
            }
          ]
        },
        "options": {}
      },
      "id": "9",
      "name": "Generate AI Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [1970, 620]
    },
    {
      "parameters": {
        "jsCode": "const source = $items('Match Knowledge', 0, 0)[0].json;\nconst response = $json;\nconst text = response.output?.[0]?.content?.[0]?.text || source.kb_answer || 'Size net bilgi verebilmemiz icin ekibimiz sizi arasin ister misiniz?';\nconst missingPhone = !source.phone && (source.callback_request || source.wants_appointment || source.intent === 'pricing');\nconst replyText = missingPhone ? `${text}\\n\\nSize hizli donus yapabilmem icin adinizi ve telefon numaranizi paylasabilir misiniz?` : text;\nconst appointmentStatus = source.wants_appointment ? 'pending_slot_selection' : 'none';\nreturn [{ json: {\n  ...source,\n  reply_text: replyText,\n  appointment_status: appointmentStatus,\n  handoff: false,\n  callback_request: source.callback_request,\n  team_summary: `Intent: ${source.intent} | Kanal: ${source.channel} | Mesaj: ${source.message_text}`\n}}];"
      },
      "id": "10",
      "name": "Build AI Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [2220, 620]
    },
    {
      "parameters": {
        "jsCode": "const item = $json;\nconst reply = item.kb_found ? `${item.kb_answer}\\n\\n${item.kb_cta || 'Isterseniz sizi yetkili ekibimize yonlendirebilirim.'}` : 'Bu konuda sizi en dogru sekilde yonlendirebilmemiz icin ekip arkadasimiz sizinle ilgilensin. Adinizi ve telefon numaranizi paylasir misiniz?';\nreturn [{ json: {\n  ...item,\n  reply_text: reply,\n  handoff: true,\n  callback_request: true,\n  appointment_status: 'manual_follow_up',\n  team_summary: `Insan devri gerekli. Kanal: ${item.channel} | Kullanici: ${item.name || item.user_id} | Mesaj: ${item.message_text}`\n}}];"
      },
      "id": "11",
      "name": "Build Handoff Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1970, 240]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.channel}}",
              "operation": "equal",
              "value2": "instagram"
            },
            {
              "value1": "={{$json.channel}}",
              "operation": "equal",
              "value2": "whatsapp"
            },
            {
              "value1": "={{$json.channel}}",
              "operation": "equal",
              "value2": "webchat"
            }
          ]
        }
      },
      "id": "12",
      "name": "Route By Channel",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [2470, 440]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/v23.0/{{$env.META_INSTAGRAM_BUSINESS_ACCOUNT_ID}}/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env.META_GRAPH_ACCESS_TOKEN}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "bodyParameters": {
          "parameters": [
            {
              "name": "recipient",
              "value": "={{ { id: $json.reply_target_id } }}"
            },
            {
              "name": "message",
              "value": "={{ { text: $json.reply_text } }}"
            }
          ]
        },
        "options": {}
      },
      "id": "13",
      "name": "Send Instagram Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [2740, 260]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/v23.0/{{$env.META_PHONE_NUMBER_ID}}/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env.META_GRAPH_ACCESS_TOKEN}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "bodyParameters": {
          "parameters": [
            {
              "name": "messaging_product",
              "value": "whatsapp"
            },
            {
              "name": "to",
              "value": "={{$json.reply_target_id}}"
            },
            {
              "name": "type",
              "value": "text"
            },
            {
              "name": "text",
              "value": "={{ { body: $json.reply_text } }}"
            }
          ]
        },
        "options": {}
      },
      "id": "14",
      "name": "Send WhatsApp Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [2740, 440]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.WEBCHAT_REPLY_ENDPOINT}}",
        "sendBody": true,
        "contentType": "json",
        "bodyParameters": {
          "parameters": [
            {
              "name": "conversation_id",
              "value": "={{$json.conversation_id}}"
            },
            {
              "name": "user_id",
              "value": "={{$json.user_id}}"
            },
            {
              "name": "reply_text",
              "value": "={{$json.reply_text}}"
            },
            {
              "name": "handoff",
              "value": "={{$json.handoff}}"
            },
            {
              "name": "appointment_status",
              "value": "={{$json.appointment_status}}"
            },
            {
              "name": "callback_request",
              "value": "={{$json.callback_request}}"
            }
          ]
        },
        "options": {}
      },
      "id": "15",
      "name": "Send Web Chat Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [2740, 620]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.wants_appointment && !!$json.phone}}",
              "operation": "true"
            }
          ]
        }
      },
      "id": "16",
      "name": "Needs Calendar Booking?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [2470, 820]
    },
    {
      "parameters": {
        "calendar": "={{$env.GOOGLE_CALENDAR_ID}}",
        "start": "={{$now.plus({ days: 1 }).set({ hour: 10, minute: 0, second: 0 }).toISO()}}",
        "end": "={{$now.plus({ days: 1 }).set({ hour: 10, minute: 30, second: 0 }).toISO()}}",
        "additionalFields": {
          "summary": "={{'Yeni gorusme - ' + ($json.name || $json.user_id)}}",
          "description": "={{'Kanal: ' + $json.channel + '\\nTelefon: ' + ($json.phone || '-') + '\\nMesaj: ' + $json.message_text}}"
        }
      },
      "id": "17",
      "name": "Create Calendar Event",
      "type": "n8n-nodes-base.googleCalendar",
      "typeVersion": 1,
      "position": [2740, 820],
      "credentials": {
        "googleCalendarOAuth2Api": {
          "id": "GOOGLE_CALENDAR_CREDENTIAL_ID",
          "name": "Google Calendar"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.TEAM_NOTIFICATION_WEBHOOK}}",
        "sendBody": true,
        "contentType": "json",
        "bodyParameters": {
          "parameters": [
            {
              "name": "conversation_id",
              "value": "={{$json.conversation_id}}"
            },
            {
              "name": "channel",
              "value": "={{$json.channel}}"
            },
            {
              "name": "name",
              "value": "={{$json.name}}"
            },
            {
              "name": "phone",
              "value": "={{$json.phone}}"
            },
            {
              "name": "summary",
              "value": "={{$json.team_summary}}"
            }
          ]
        },
        "options": {}
      },
      "id": "18",
      "name": "Notify Team",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [2740, 1000]
    }
  ],
  "connections": {
    "Execute Workflow Trigger": {
      "main": [[{ "node": "Prepare Conversation", "type": "main", "index": 0 }]]
    },
    "Prepare Conversation": {
      "main": [
        [{ "node": "Log Lead Row", "type": "main", "index": 0 }],
        [{ "node": "Load Knowledge Base", "type": "main", "index": 0 }]
      ]
    },
    "Log Lead Row": {
      "main": [[{ "node": "Merge Context", "type": "main", "index": 0 }]]
    },
    "Load Knowledge Base": {
      "main": [[{ "node": "Merge Context", "type": "main", "index": 1 }]]
    },
    "Merge Context": {
      "main": [[{ "node": "Match Knowledge", "type": "main", "index": 0 }]]
    },
    "Match Knowledge": {
      "main": [[{ "node": "Needs Human?", "type": "main", "index": 0 }]]
    },
    "Needs Human?": {
      "main": [
        [{ "node": "Build Handoff Response", "type": "main", "index": 0 }],
        [{ "node": "Knowledge Found?", "type": "main", "index": 0 }]
      ]
    },
    "Knowledge Found?": {
      "main": [
        [{ "node": "Generate AI Reply", "type": "main", "index": 0 }],
        [{ "node": "Build Handoff Response", "type": "main", "index": 0 }]
      ]
    },
    "Generate AI Reply": {
      "main": [[{ "node": "Build AI Response", "type": "main", "index": 0 }]]
    },
    "Build AI Response": {
      "main": [
        [{ "node": "Route By Channel", "type": "main", "index": 0 }],
        [{ "node": "Needs Calendar Booking?", "type": "main", "index": 0 }],
        [{ "node": "Notify Team", "type": "main", "index": 0 }]
      ]
    },
    "Build Handoff Response": {
      "main": [
        [{ "node": "Route By Channel", "type": "main", "index": 0 }],
        [{ "node": "Notify Team", "type": "main", "index": 0 }]
      ]
    },
    "Route By Channel": {
      "main": [
        [{ "node": "Send Instagram Reply", "type": "main", "index": 0 }],
        [{ "node": "Send WhatsApp Reply", "type": "main", "index": 0 }],
        [{ "node": "Send Web Chat Reply", "type": "main", "index": 0 }]
      ]
    },
    "Needs Calendar Booking?": {
      "main": [
        [{ "node": "Create Calendar Event", "type": "main", "index": 0 }],
        []
      ]
    }
  },
  "settings": { "executionOrder": "v1" },
  "pinData": {},
  "versionId": "conversation-orchestrator-v1",
  "meta": { "templateCredsSetupCompleted": false },
  "id": "conversation-orchestrator"
}
