{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "279428be-dbfb-44d8-8a3b-fe215e815ab2",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1280,
        80
      ],
      "parameters": {
        "width": 480,
        "height": 896,
        "content": "## Untitled workflow\n\n### How it works\n\n1. Receives incoming data via a webhook.\n2. Parses and determines if the message is a first-time interaction.\n3. Processes the message using speech-to-text if necessary.\n4. Determines if escalation to a human agent is needed or handles the request with AI.\n5. Transforms responses back to text and logs lead information in Google Sheets.\n\n### Setup steps\n\n- [ ] Configure OpenAI API credentials for AI responses.\n- [ ] Set up the Webhook endpoint and ensure it is reachable.\n- [ ] Configure API access for Sarvam STT and TTS services.\n- [ ] Connect and authorize Google Sheets access for logging leads.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "5760d85a-4a13-4b64-a18f-a76d3c4084c9",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -720,
        256
      ],
      "parameters": {
        "color": 7,
        "width": 416,
        "height": 304,
        "content": "## Receive and parse input\n\nHandles the receipt of incoming data through a webhook and parses it."
      },
      "typeVersion": 1
    },
    {
      "id": "d3be253b-2cf4-41b7-8863-383e776f9f6f",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -272,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 608,
        "content": "## Determine interaction type\n\nChecks if the interaction is a first-time interaction and directs flow based on condition."
      },
      "typeVersion": 1
    },
    {
      "id": "5b28d55f-e5b8-4fc7-9c72-4bd5e976acc4",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        160,
        144
      ],
      "parameters": {
        "color": 7,
        "width": 624,
        "height": 688,
        "content": "## Speech-to-text processing\n\nProcesses speech-to-text conversion and evaluates if escalation is necessary."
      },
      "typeVersion": 1
    },
    {
      "id": "f9818865-a8a1-4bef-b315-aa010ba9766a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        288
      ],
      "parameters": {
        "color": 7,
        "width": 880,
        "height": 384,
        "content": "## Merge and respond\n\nMerges different response paths and sends final response back to the requester."
      },
      "typeVersion": 1
    },
    {
      "id": "4a08a3f8-4062-4c2e-a704-c7400470d7af",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1488,
        704
      ],
      "parameters": {
        "color": 7,
        "width": 432,
        "height": 304,
        "content": "## Log lead information\n\nPrepares and logs lead information into Google Sheets."
      },
      "typeVersion": 1
    },
    {
      "id": "4784272f-1142-42e8-876f-fbc2e75abed3",
      "name": "OpenAI Model Message",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        592,
        656
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o",
          "cachedResultName": "GPT-4O"
        },
        "options": {
          "maxTokens": 400
        },
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "=\u0ba8\u0bc0 Kavya. \u0ba8\u0bc0 \u0b92\u0bb0\u0bc1 friendly real estate assistant.\n\n== \u0b89\u0ba9\u0bcd \u0baa\u0bc7\u0b9a\u0bcd\u0b9a\u0bc1 style ==\n- \u0b8e\u0baa\u0bcd\u0baa\u0bcb\u0ba4\u0bc1\u0bae\u0bcd simple, friendly Tamil-\u0bb2\u0bcd \u0baa\u0bc7\u0b9a\u0bc1.\n- \u0b92\u0bb5\u0bcd\u0bb5\u0bca\u0bb0\u0bc1 reply-\u0baf\u0bc1\u0bae\u0bcd short \u0b86\u0b95 \u0b87\u0bb0\u0bc1 (max 2 sentences).\n- Customer \u0b9a\u0bca\u0ba9\u0bcd\u0ba9\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0bc1 first \u0b92\u0bb0\u0bc1 small acknowledgment \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bc1.\n- \u0b85\u0baa\u0bcd\u0baa\u0bc1\u0bb1\u0bae\u0bcd \u0ba4\u0bbe\u0ba9\u0bcd next question \u0b95\u0bc7\u0bb3\u0bcd.\n- Customer \u0baa\u0bc6\u0baf\u0bb0\u0bcd \u0ba4\u0bc6\u0bb0\u0bbf\u0ba8\u0bcd\u0ba4\u0ba4\u0bc1\u0bae\u0bcd \u0b85\u0ba8\u0bcd\u0ba4 \u0baa\u0bc6\u0baf\u0bb0\u0bc8 \u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0bbf \u0baa\u0bc7\u0b9a\u0bc1.\n\n== \u0bae\u0bbf\u0b95 \u0bae\u0bc1\u0b95\u0bcd\u0b95\u0bbf\u0baf output rule ==\n- Plain text \u0bae\u0b9f\u0bcd\u0b9f\u0bc1\u0bae\u0bcd return \u0baa\u0ba3\u0bcd\u0ba3\u0bc1.\n- JSON format, code block, quotes-wrapped object \u0bae\u0bbe\u0ba4\u0bbf\u0bb0\u0bbf output \u0b95\u0bca\u0b9f\u0bc1\u0b95\u0bcd\u0b95\u0bbe\u0ba4\u0bc7.\n- \u0b89\u0ba4\u0bbe\u0bb0\u0ba3\u0bae\u0bcd: {\"role\":\"assistant\",\"content\":\"...\"} \u0bae\u0bbe\u0ba4\u0bbf\u0bb0\u0bbf \u0b92\u0bb0\u0bc1\u0baa\u0bcb\u0ba4\u0bc1\u0bae\u0bcd \u0b8e\u0bb4\u0bc1\u0ba4\u0bbe\u0ba4\u0bc7.\n\n== \u0b8e\u0baa\u0bcd\u0baa\u0b9f\u0bbf \u0baa\u0bc7\u0b9a\u0ba3\u0bc1\u0bae\u0bcd ==\n- Numbers Tamil words-\u0bb2\u0bcd \u0b8e\u0bb4\u0bc1\u0ba4\u0ba3\u0bc1\u0bae\u0bcd.\n- \u0b8e\u0ba8\u0bcd\u0ba4 symbols \u0bb5\u0bc7\u0ba3\u0bcd\u0b9f\u0bbe\u0bae\u0bcd.\n- unnecessary English short forms avoid \u0baa\u0ba3\u0bcd\u0ba3\u0bc1.\n- \"plot\" \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bbe\u0ba4\u0bc7; \"\u0bae\u0ba9\u0bc8\" \u0b9a\u0bca\u0bb2\u0bcd.\n- \"site visit\" \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bbe\u0ba4\u0bc7; \"\u0ba8\u0bc7\u0bb0\u0bbf\u0bb2\u0bcd \u0bb5\u0ba8\u0bcd\u0ba4\u0bc1 \u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95\" \u0b9a\u0bca\u0bb2\u0bcd.\n- \"ready to move\" \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bbe\u0ba4\u0bc7; \"\u0b87\u0baa\u0bcd\u0baa\u0bcb\u0bb5\u0bc7 \u0b95\u0bc1\u0b9f\u0bbf \u0baa\u0bcb\u0b95\u0bb2\u0bbe\u0bae\u0bcd\" \u0b9a\u0bca\u0bb2\u0bcd.\n- \"under construction\" \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bbe\u0ba4\u0bc7; \"\u0b95\u0b9f\u0bcd\u0b9f\u0bbf \u0b95\u0bca\u0ba3\u0bcd\u0b9f\u0bbf\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bbe\u0b99\u0bcd\u0b95\" \u0b9a\u0bca\u0bb2\u0bcd.\n\n== Properties ==\n\u0baa\u0bbf\u0bb0\u0bc6\u0bb8\u0bcd\u0b9f\u0bbf\u0b9c\u0bcd \u0b95\u0bbe\u0bb0\u0bcd\u0b9f\u0ba9\u0bcd\u0bb8\u0bcd, \u0b93. \u0b8e\u0bae\u0bcd. \u0b86\u0bb0\u0bcd.-\u0bb2\u0bcd \u0b87\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bc1.\n\u0b87\u0bb0\u0ba3\u0bcd\u0b9f\u0bc1 \u0baa\u0bbf \u0b8e\u0b9a\u0bcd \u0b95\u0bc7 \u0b85\u0bb1\u0bc1\u0baa\u0ba4\u0bcd\u0ba4\u0bbf \u0b90\u0ba8\u0bcd\u0ba4\u0bc1 \u0bb2\u0b9f\u0bcd\u0b9a\u0bae\u0bcd.\n\u0bae\u0bc2\u0ba9\u0bcd\u0bb1\u0bc1 \u0baa\u0bbf \u0b8e\u0b9a\u0bcd \u0b95\u0bc7 \u0b8e\u0ba3\u0bcd\u0baa\u0ba4\u0bcd\u0ba4\u0bbf \u0b90\u0ba8\u0bcd\u0ba4\u0bc1 \u0bb2\u0b9f\u0bcd\u0b9a\u0bae\u0bcd.\n\u0b87\u0baa\u0bcd\u0baa\u0bcb\u0bb5\u0bc7 \u0b95\u0bc1\u0b9f\u0bbf \u0baa\u0bcb\u0b95\u0bb2\u0bbe\u0bae\u0bcd.\n\n\u0b95\u0bcb\u0bb2\u0bcd\u0b9f\u0ba9\u0bcd \u0b9a\u0bbf\u0b9f\u0bcd\u0b9f\u0bbf, \u0baa\u0bcb\u0bb0\u0bc2\u0bb0\u0bcd-\u0bb2\u0bcd \u0b87\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bc1.\n\u0bae\u0ba9\u0bc8 \u0bb5\u0bbf\u0bb2\u0bc8 \u0bae\u0bc1\u0baa\u0bcd\u0baa\u0ba4\u0bcd\u0ba4\u0bbf \u0b90\u0ba8\u0bcd\u0ba4\u0bc1 \u0bb2\u0b9f\u0bcd\u0b9a\u0bae\u0bcd-\u0bb2\u0bcd \u0b87\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bc1 \u0ba4\u0bca\u0b9f\u0b99\u0bcd\u0b95\u0bc1\u0ba4\u0bc1.\n\u0bb0\u0bc7\u0bb0\u0bbe \u0b85\u0ba9\u0bc1\u0bae\u0ba4\u0bbf \u0b95\u0bbf\u0b9f\u0bc8\u0b9a\u0bcd\u0b9a\u0bbf\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bc1.\n\n\u0bb8\u0bcd\u0b95\u0bc8\u0bb2\u0bc8\u0ba9\u0bcd \u0b9f\u0bb5\u0bb0\u0bcd\u0bb8\u0bcd, \u0b85\u0ba3\u0bcd\u0ba3\u0bbe \u0ba8\u0b95\u0bb0\u0bcd-\u0bb2\u0bcd \u0b87\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bc1.\n\u0b87\u0bb0\u0ba3\u0bcd\u0b9f\u0bc1 \u0baa\u0bbf \u0b8e\u0b9a\u0bcd \u0b95\u0bc7 \u0b92\u0bb0\u0bc1 \u0b95\u0bcb\u0b9f\u0bbf\u0baf\u0bc7 \u0b87\u0bb0\u0bc1\u0baa\u0ba4\u0bc1 \u0bb2\u0b9f\u0bcd\u0b9a\u0bae\u0bcd.\n\u0b95\u0b9f\u0bcd\u0b9f\u0bbf \u0b95\u0bca\u0ba3\u0bcd\u0b9f\u0bbf\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bbe\u0b99\u0bcd\u0b95.\n\n== Office info ==\n\u0ba4\u0bbf\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0bae\u0bc1\u0ba4\u0bb2\u0bcd \u0b9a\u0ba9\u0bbf \u0bb5\u0bb0\u0bc8, \u0b95\u0bbe\u0bb2\u0bc8 \u0b92\u0ba9\u0bcd\u0baa\u0ba4\u0bc1 \u0bae\u0bc1\u0ba4\u0bb2\u0bcd \u0bae\u0bbe\u0bb2\u0bc8 \u0b8f\u0bb4\u0bc1 \u0bae\u0ba3\u0bbf \u0bb5\u0bb0\u0bc8 open.\n\u0ba4\u0bca\u0bb2\u0bc8\u0baa\u0bc7\u0b9a\u0bbf \u0b9a\u0bc0\u0bb0\u0bcb \u0ba8\u0bbe\u0ba9\u0bcd\u0b95\u0bc1 \u0ba8\u0bbe\u0ba9\u0bcd\u0b95\u0bc1 XXXX XXXX.\n\n== Conversation flow ==\nStep 1:\n\u0bae\u0bc1\u0ba4\u0bb2\u0bbf\u0bb2\u0bcd user query-\u0b95\u0bcd\u0b95\u0bc1 acknowledge \u0baa\u0ba3\u0bcd\u0ba3\u0bc1.\n\u0b85\u0ba4\u0bc1\u0b95\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc1\u0bb1\u0bae\u0bcd only \u0b92\u0bb0\u0bc1 question \u0b95\u0bc7\u0bb3\u0bcd:\n\"\u0b8e\u0ba8\u0bcd\u0ba4 area \u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95\u0bbf\u0bb1\u0bc0\u0b99\u0bcd\u0b95?\" \u0b85\u0bb2\u0bcd\u0bb2\u0ba4\u0bc1 \"\u0baa\u0b9f\u0bcd\u0b9c\u0bc6\u0b9f\u0bcd \u0b8e\u0ba9\u0bcd\u0ba9?\"\n\nStep 2:\nUser \u0baa\u0ba4\u0bbf\u0bb2\u0bcd \u0b95\u0bca\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0ba4\u0bc1\u0bae\u0bcd acknowledge \u0baa\u0ba3\u0bcd\u0ba3\u0bbf property suggest \u0baa\u0ba3\u0bcd\u0ba3\u0bc1:\n\"\u0b9a\u0bb0\u0bbf, [property name]-\u0bb2\u0bcd [type] \u0b87\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bc1. \u0bb5\u0bbf\u0bb2\u0bc8 [amount]. \u0ba8\u0bc7\u0bb0\u0bbf\u0bb2\u0bcd \u0bb5\u0ba8\u0bcd\u0ba4\u0bc1 \u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95\u0bb2\u0bbe\u0bae\u0bbe?\"\n\nStep 3:\nUser interest confirm \u0baa\u0ba3\u0bcd\u0ba3\u0bbf\u0ba9\u0bbe \u0b87\u0ba4\u0bc7 order strict-\u0b86 follow \u0baa\u0ba3\u0bcd\u0ba3\u0bc1:\n- \u0bae\u0bc1\u0ba4\u0bb2\u0bbf\u0bb2\u0bcd \u0baa\u0bc6\u0baf\u0bb0\u0bcd \u0b95\u0bc7\u0bb3\u0bcd\n- \u0baa\u0bc6\u0baf\u0bb0\u0bcd \u0b95\u0bbf\u0b9f\u0bc8\u0ba4\u0bcd\u0ba4\u0ba4\u0bc1\u0bae\u0bcd \u0b85\u0ba8\u0bcd\u0ba4 \u0baa\u0bc6\u0baf\u0bb0\u0bc8 \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bbf acknowledge \u0baa\u0ba3\u0bcd\u0ba3\u0bbf \u0ba8\u0bbe\u0bb3\u0bcd \u0b95\u0bc7\u0bb3\u0bcd\n- \u0ba8\u0bbe\u0bb3\u0bcd \u0b95\u0bbf\u0b9f\u0bc8\u0ba4\u0bcd\u0ba4\u0ba4\u0bc1\u0bae\u0bcd acknowledge \u0baa\u0ba3\u0bcd\u0ba3\u0bbf \u0ba4\u0bca\u0bb2\u0bc8\u0baa\u0bc7\u0b9a\u0bbf \u0b8e\u0ba3\u0bcd \u0b95\u0bc7\u0bb3\u0bcd\n\u0b92\u0bb0\u0bc7 reply-\u0bb2\u0bcd \u0b8e\u0bb2\u0bcd\u0bb2\u0bbe details\u0baf\u0bc1\u0bae\u0bcd \u0b95\u0bc7\u0b9f\u0bcd\u0b95\u0bbe\u0ba4\u0bc7.\n\nStep 4:\nComplex question \u0b87\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bbe:\n\"\u0baa\u0bc1\u0bb0\u0bbf\u0ba8\u0bcd\u0ba4\u0ba4\u0bc1, \u0ba8\u0bbe\u0ba9\u0bcd \u0b89\u0b99\u0bcd\u0b95\u0bb3\u0bc8 \u0b8e\u0b99\u0bcd\u0b95\u0bb3\u0bcd agent-\u0b95\u0bbf\u0b9f\u0bcd\u0b9f connect \u0b9a\u0bc6\u0baf\u0bcd\u0b95\u0bbf\u0bb1\u0bc7\u0ba9\u0bcd. \u0b9a\u0bbf\u0bb2 \u0ba8\u0bbf\u0bae\u0bbf\u0b9f\u0bae\u0bcd \u0b87\u0bb0\u0bc1\u0b99\u0bcd\u0b95.\"\n\n== name capture example ==\nUser: \u0baa\u0bbe\u0bb0\u0bc1\nKavya: \u0baa\u0bbe\u0bb0\u0bc1, \u0bb0\u0bca\u0bae\u0bcd\u0baa \u0ba8\u0ba9\u0bcd\u0bb1\u0bbf. \u0b8e\u0ba8\u0bcd\u0ba4 \u0ba8\u0bbe\u0bb3\u0bcd \u0bb5\u0bb0 \u0bb5\u0b9a\u0ba4\u0bbf\u0baf\u0bbe\u0b95 \u0b87\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bc1\u0bae\u0bcd?\n\nUser: \u0b9e\u0bbe\u0baf\u0bbf\u0bb1\u0bc1\nKavya: \u0b9a\u0bb0\u0bbf \u0baa\u0bbe\u0bb0\u0bc1, \u0b9e\u0bbe\u0baf\u0bbf\u0bb1\u0bc1 note \u0baa\u0ba3\u0bcd\u0ba3\u0bbf\u0b9f\u0bcd\u0b9f\u0bc7\u0ba9\u0bcd. \u0b89\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0ba4\u0bca\u0bb2\u0bc8\u0baa\u0bc7\u0b9a\u0bbf \u0b8e\u0ba3\u0bcd \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bc1\u0b99\u0bcd\u0b95?"
            },
            {
              "content": "={{ JSON.stringify($json.conversationHistory) }}"
            }
          ]
        },
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "9bdbae03-af0b-4f4e-9a82-85908236a368",
      "name": "When POST to Tamil Agent",
      "type": "n8n-nodes-base.webhook",
      "onError": "continueRegularOutput",
      "position": [
        -672,
        384
      ],
      "parameters": {
        "path": "tamil-voice-agent-v2",
        "options": {
          "rawBody": true
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2
    },
    {
      "id": "cc95b62e-cf7c-403c-b539-d94fb8382a64",
      "name": "Parse Webhook Input",
      "type": "n8n-nodes-base.code",
      "position": [
        -448,
        384
      ],
      "parameters": {
        "jsCode": "// ---------- SAFE INPUT PARSER ----------\n\nconst input = $input.first();\n\n// \u2705 FIX: preserve binary\nconst binary = input.binary || {};\n\n// Normalize browser codec MIME values (e.g. audio/webm;codecs=opus) to base types accepted by STT.\nif (binary.audio?.mimeType && typeof binary.audio.mimeType === 'string') {\n  const normalizedMime = binary.audio.mimeType.split(';')[0].trim().toLowerCase();\n  binary.audio.mimeType = normalizedMime;\n}\n\nconst body = input.json.body || input.json || {};\n\n// ---------- METADATA ----------\nconst callerPhone = body.caller_phone || body.phone || 'unknown';\nconst callSid = body.call_sid || body.session_id || `call_${Date.now()}`;\nconst callType = body.call_type || 'inbound';\n\n// ---------- CONVERSATION ----------\nlet conversationHistory = body.conversation_history || [];\n\nif (typeof conversationHistory === 'string') {\n  try {\n    conversationHistory = JSON.parse(conversationHistory);\n  } catch (e) {\n    conversationHistory = [];\n  }\n}\n\nif (!Array.isArray(conversationHistory)) {\n  conversationHistory = [];\n}\n\n// ---------- INPUT TEXT ----------\nlet inputText = body.text || body.message || null;\n\nif (!inputText && conversationHistory.length > 0) {\n  const lastMsg = conversationHistory[conversationHistory.length - 1];\n  if (lastMsg?.role === 'user') {\n    inputText = lastMsg.content;\n  }\n}\n\n// ---------- AUDIO DETECTION ----------\nconst hasBinaryAudio = !!binary.audio;\n\nlet audioBase64 = body.audio_base64 || null;\nlet audioUrl = body.audio_url || null;\n\nconst hasAudio = !!(audioBase64 || audioUrl || hasBinaryAudio);\nconst isTextOnly = !hasAudio && !!inputText;\n\n// ---------- FIRST TURN ----------\nconst hasUserInput = !!inputText || hasAudio;\nconst hasHistory = conversationHistory.length > 0;\n\nconst isFirstTurn = !hasHistory && !hasUserInput;\n\n// ---------- OUTPUT (CRITICAL FIX) ----------\nreturn {\n  json: {\n    callerPhone,\n    callSid,\n    callType,\n\n    conversationHistory,\n\n    inputText,\n    audioBase64,\n    audioUrl,\n    hasBinaryAudio,\n\n    hasAudio,\n    isTextOnly,\n    isFirstTurn,\n\n    timestamp: new Date().toISOString()\n  },\n\n  // \ud83d\udd25 THIS IS THE FIX\n  binary: binary\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "7620c881-56f9-4325-8304-af91fde9179a",
      "name": "If First Turn",
      "type": "n8n-nodes-base.if",
      "position": [
        -224,
        384
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check-first-turn",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.isFirstTurn }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "cb42a9c0-b105-47de-af39-ccfbc2be4417",
      "name": "Generate Welcome Message",
      "type": "n8n-nodes-base.code",
      "position": [
        -16,
        240
      ],
      "parameters": {
        "jsCode": "// Generate welcome message for first turn (no STT needed)\nconst callType = $('Parse Webhook Input').first().json.callType;\nconst callerPhone = $('Parse Webhook Input').first().json.callerPhone;\n\nlet welcomeText = '';\nif (callType === 'outbound') {\n  welcomeText = '\u0bb9\u0bb2\u0bcb! \u0ba8\u0bbe\u0ba9\u0bcd \u0b89\u0b99\u0bcd\u0b95\u0bb3\u0bcd real estate assistant Kavya. \u0ba8\u0bc0\u0b99\u0bcd\u0b95\u0bb3\u0bcd property enquiry form fill \u0baa\u0ba3\u0bcd\u0ba3\u0bbf\u0bb0\u0bc1\u0ba8\u0bcd\u0ba4\u0bc0\u0b99\u0bcd\u0b95. \u0b8e\u0ba9\u0bcd\u0ba9 \u0bae\u0bbe\u0ba4\u0bbf\u0bb0\u0bbf property \u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95\u0bbf\u0bb1\u0bc0\u0b99\u0bcd\u0b95?';\n} else {\n  welcomeText = '\u0bb9\u0bb2\u0bcb! \u0ba8\u0bc0\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0b8e\u0b99\u0bcd\u0b95\u0bb3\u0bcd real estate team-\u0b95\u0bcd\u0b95\u0bc1 call \u0baa\u0ba3\u0bcd\u0ba3\u0bbf\u0bb0\u0bc1\u0b95\u0bcd\u0b95\u0bc0\u0b99\u0bcd\u0b95. Property details \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bb2\u0bbe\u0bae\u0bcd, site visit book \u0baa\u0ba3\u0bcd\u0ba3\u0bb2\u0bbe\u0bae\u0bcd. \u0b87\u0baa\u0bcd\u0baa\u0bcb \u0b8e\u0ba9\u0bcd\u0ba9 help \u0bb5\u0bc7\u0ba3\u0bc1\u0bae\u0bcd?';\n}\n\nreturn {\n  transcribedText: null,\n  agentResponse: welcomeText,\n  isFirstTurn: true,\n  callerPhone,\n  callSid: $('Parse Webhook Input').first().json.callSid,\n  conversationHistory: [],\n  skipSTT: true\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "9e695ff3-2905-44e2-b1a7-4f98f2ab4d4e",
      "name": "Post to Sarvam STT",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        -16,
        528
      ],
      "parameters": {
        "url": "https://api.sarvam.ai/speech-to-text",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "multipart-form-data",
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "audio"
            },
            {
              "name": "model",
              "value": "saarika:v2.5"
            },
            {
              "name": "language_code",
              "value": "ta-IN"
            },
            {
              "name": "with_timestamps",
              "value": "false"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "api-subscription-key",
              "value": "={{$env.SARVAM_API_KEY}}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "77631eaf-2521-4b0d-9e57-035d08c78579",
      "name": "Prepare STT Results",
      "type": "n8n-nodes-base.code",
      "position": [
        208,
        528
      ],
      "parameters": {
        "jsCode": "// Prepare STT result and conversation context with robust transcript parsing\nconst sttResponse = $input.first().json;\nconst parsedInput = $('Parse Webhook Input').first().json;\n\nconst extractTranscript = (resp) => {\n  if (!resp || typeof resp !== 'object') return '';\n  if (typeof resp.transcript === 'string') return resp.transcript.trim();\n  if (typeof resp.text === 'string') return resp.text.trim();\n  if (typeof resp.output === 'string') return resp.output.trim();\n  if (Array.isArray(resp.results)) {\n    for (const item of resp.results) {\n      if (typeof item?.transcript === 'string' && item.transcript.trim()) {\n        return item.transcript.trim();\n      }\n    }\n  }\n  return '';\n};\n\nconst transcribedText = extractTranscript(sttResponse);\nconst conversationHistory = Array.isArray(parsedInput.conversationHistory)\n  ? [...parsedInput.conversationHistory]\n  : [];\n\nconst sttFailed = transcribedText.length === 0;\n\n// Add user message only when STT produced text\nif (!sttFailed) {\n  conversationHistory.push({\n    role: 'user',\n    content: transcribedText\n  });\n}\n\n// Detect escalation keywords\nconst escalationKeywords = [\n  'manager', 'human', 'person', 'urgent', 'complaint',\n  '\u0bae\u0bc7\u0bb2\u0bbe\u0bb3\u0bb0\u0bcd', '\u0b86\u0bb3\u0bcd', '\u0b85\u0bb5\u0b9a\u0bb0\u0bae\u0bcd', 'problem'\n];\nconst needsEscalation = !sttFailed && escalationKeywords.some((kw) =>\n  transcribedText.toLowerCase().includes(kw.toLowerCase())\n);\n\nreturn [{\n  json: {\n    transcribedText,\n    conversationHistory,\n    callerPhone: parsedInput.callerPhone,\n    callSid: parsedInput.callSid,\n    needsEscalation,\n    sttFailed,\n    sttRaw: sttResponse,\n    isFirstTurn: false\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "3413a3ed-e1be-4db6-be7e-0d8ddbe3844b",
      "name": "Check for Escalation",
      "type": "n8n-nodes-base.if",
      "position": [
        432,
        528
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": false,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "escalation-check",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.needsEscalation }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "b097ba8c-1191-4b8a-9924-60a51a7cfc51",
      "name": "Handle Escalation",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        272
      ],
      "parameters": {
        "jsCode": "// Escalation path \u2014 notify human agent\nconst data = $input.first().json;\n\nconst escalationResponse = '\u0baa\u0bc1\u0bb0\u0bbf\u0ba8\u0bcd\u0ba4\u0ba4\u0bc1. \u0ba8\u0bbe\u0ba9\u0bcd \u0b87\u0baa\u0bcd\u0baa\u0bcb\u0ba4\u0bc1 \u0b89\u0b99\u0bcd\u0b95\u0bb3\u0bc8 \u0b8e\u0b99\u0bcd\u0b95\u0bb3\u0bcd human agent-\u0b95\u0bbf\u0b9f\u0bcd\u0b9f transfer \u0b9a\u0bc6\u0baf\u0bcd\u0b95\u0bbf\u0bb1\u0bc7\u0ba9\u0bcd. \u0b9a\u0bbf\u0bb2 \u0ba8\u0bbf\u0bae\u0bbf\u0b9f\u0b99\u0bcd\u0b95\u0bb3\u0bcd hold-\u0bb2\u0bcd \u0b87\u0bb0\u0bc1\u0b99\u0bcd\u0b95\u0bb3\u0bcd.';\n\n// In production: trigger SMS/WhatsApp alert to human agent here\nreturn [{\n  json: {\n    agentResponse: escalationResponse,\n    transcribedText: data.transcribedText,\n    conversationHistory: data.conversationHistory,\n    callerPhone: data.callerPhone,\n    callSid: data.callSid,\n    isEscalated: true,\n    escalationAlert: {\n      phone: data.callerPhone,\n      transcript: data.transcribedText,\n      timestamp: new Date().toISOString()\n    }\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "37619e99-36e7-4d83-936a-aa37e5fe7fcc",
      "name": "Merge Response Streams",
      "type": "n8n-nodes-base.code",
      "position": [
        880,
        400
      ],
      "parameters": {
        "jsCode": "function safeGet(nodeName) {\n  try {\n    const data = $(nodeName).all();\n    return data.length ? data[0].json : null;\n  } catch (e) {\n    return null;\n  }\n}\n\nconst escalationData = safeGet('\ud83d\udea8 Escalation Handler');\nconst llmData = safeGet('Message a model1');\nconst sttData = safeGet('\ud83d\udcdd Process STT');\nconst welcomeData = safeGet('\ud83d\udc4b Welcome Message');\n\nlet agentResponse = '';\nlet conversationHistory = sttData?.conversationHistory || welcomeData?.conversationHistory || [];\nlet callerPhone = sttData?.callerPhone || welcomeData?.callerPhone;\nlet callSid = sttData?.callSid || welcomeData?.callSid;\nlet isEscalated = false;\nlet isFirstTurn = welcomeData?.isFirstTurn || false;\n\nif (isFirstTurn && welcomeData) {\n  agentResponse = welcomeData.agentResponse;\n} else if (sttData?.sttFailed) {\n  agentResponse = '\u0bae\u0ba9\u0bcd\u0ba9\u0bbf\u0b95\u0bcd\u0b95\u0bb5\u0bc1\u0bae\u0bcd, \u0b89\u0b99\u0bcd\u0b95\u0bb3\u0bcd voice clear-\u0b86 \u0b95\u0bc7\u0b9f\u0bcd\u0b95\u0bb5\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8. \u0b95\u0bca\u0b9e\u0bcd\u0b9a\u0bae\u0bcd \u0bae\u0bc6\u0ba4\u0bc1\u0bb5\u0bbe\u0b95 \u0bae\u0bb1\u0bc1\u0baa\u0b9f\u0bbf\u0baf\u0bc1\u0bae\u0bcd \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bc1\u0b99\u0bcd\u0b95\u0bb3\u0bcd, \u0b85\u0bb2\u0bcd\u0bb2\u0ba4\u0bc1 text-\u0b86 \u0b85\u0ba9\u0bc1\u0baa\u0bcd\u0baa\u0bb2\u0bbe\u0bae\u0bcd.';\n  conversationHistory = [\n    ...conversationHistory,\n    { role: 'assistant', content: agentResponse }\n  ];\n} else if (escalationData?.isEscalated) {\n  agentResponse = escalationData.agentResponse;\n  isEscalated = true;\n} else if (llmData) {\n  const extractAssistantText = (data) => {\n    if (typeof data?.text === 'string' && data.text.trim()) return data.text.trim();\n    if (typeof data?.output === 'string' && data.output.trim()) return data.output.trim();\n\n    const output0 = Array.isArray(data?.output) ? data.output[0] : null;\n    const outContent0 = Array.isArray(output0?.content) ? output0.content[0] : null;\n    if (typeof outContent0?.text === 'string' && outContent0.text.trim()) return outContent0.text.trim();\n\n    const choiceText = data?.choices?.[0]?.message?.content;\n    if (typeof choiceText === 'string' && choiceText.trim()) return choiceText.trim();\n\n    const msgContent0 = Array.isArray(data?.content) ? data.content[0] : null;\n    if (typeof msgContent0?.text === 'string' && msgContent0.text.trim()) return msgContent0.text.trim();\n\n    return '';\n  };\n\n  agentResponse = extractAssistantText(llmData) || 'Sorry, clear-ah \u0b95\u0bc7\u0b9f\u0bcd\u0b95\u0bb2. \u0b87\u0ba9\u0bcd\u0ba9\u0bca\u0bb0\u0bc1 \u0ba4\u0b9f\u0bb5\u0bc8 \u0b9a\u0bca\u0bb2\u0bcd\u0bb2\u0bc1\u0b99\u0bcd\u0b95.';\n\n  // Unwrap accidental JSON-string responses like {\"role\":\"assistant\",\"content\":\"...\"}\n  const maybeJsonText = agentResponse.trim();\n  if (maybeJsonText.startsWith('{') && maybeJsonText.endsWith('}')) {\n    try {\n      const parsed = JSON.parse(maybeJsonText);\n      if (typeof parsed?.content === 'string' && parsed.content.trim()) {\n        agentResponse = parsed.content.trim();\n      }\n    } catch (e) {\n      // Keep original text when parsing fails\n    }\n  }\n\n  // Keep spoken output simple, friendly Tamil/Tanglish (avoid very formal Tamil words).\n  agentResponse = agentResponse\n    .replaceAll('\u0b89\u0ba4\u0bb5\u0b9f\u0bcd\u0b9f\u0bc1\u0bae\u0bcd', 'help \u0baa\u0ba3\u0bcd\u0ba3\u0bb2\u0bbe\u0bae\u0bcd')\n    .replaceAll('\u0b89\u0ba4\u0bb5\u0bb2\u0bbe\u0bae\u0bcd', 'help \u0baa\u0ba3\u0bcd\u0ba3\u0bb2\u0bbe\u0bae\u0bcd')\n    .replaceAll('\u0b85\u0bb5\u0b9a\u0bbf\u0baf\u0bae\u0bcd', '\u0ba4\u0bc7\u0bb5\u0bc8\u0ba9\u0bbe')\n    .replaceAll('\u0ba4\u0bca\u0b9f\u0bb0\u0bb5\u0bc1\u0bae\u0bcd', 'continue \u0baa\u0ba3\u0bcd\u0ba3\u0bb2\u0bbe\u0bae\u0bcd')\n    .replaceAll('\u0bb5\u0ba3\u0b95\u0bcd\u0b95\u0bae\u0bcd', '\u0bb9\u0bb2\u0bcb');\n\n  conversationHistory = [\n    ...conversationHistory,\n    { role: 'assistant', content: agentResponse }\n  ];\n} else {\n  agentResponse = '\u0bae\u0ba9\u0bcd\u0ba9\u0bbf\u0b95\u0bcd\u0b95\u0bb5\u0bc1\u0bae\u0bcd, response generate \u0b86\u0b95\u0bb5\u0bbf\u0bb2\u0bcd\u0bb2\u0bc8. \u0bae\u0bc0\u0ba3\u0bcd\u0b9f\u0bc1\u0bae\u0bcd \u0bae\u0bc1\u0baf\u0bb1\u0bcd\u0b9a\u0bbf \u0b9a\u0bc6\u0baf\u0bcd\u0baf\u0bc1\u0b99\u0bcd\u0b95\u0bb3\u0bcd.';\n}\n\nreturn [{\n  json: {\n    agentResponse,\n    conversationHistory,\n    callerPhone,\n    callSid,\n    isEscalated,\n    transcribedText: sttData?.transcribedText || null,\n    sttFailed: sttData?.sttFailed || false,\n    sttRaw: sttData?.sttRaw || null\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "5fdc4395-d70b-4433-9b71-6e0c393a0471",
      "name": "Post to Sarvam TTS",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1104,
        464
      ],
      "parameters": {
        "url": "https://api.sarvam.ai/text-to-speech",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"text\": {{ JSON.stringify($json.agentResponse) }},\n  \"target_language_code\": \"ta-IN\",\n  \"speaker\": \"ishita\",\n  \"model\": \"bulbul:v3\",\n  \"pace\": 0.95,\n  \"speech_sample_rate\": 22050,\n  \"enable_preprocessing\": true,\n  \"temperature\": 0.60\n}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "api-subscription-key",
              "value": "={{$env.SARVAM_API_KEY}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "330f377b-daf3-437f-8fc3-9d5a8787b44d",
      "name": "Build Response Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        1344,
        512
      ],
      "parameters": {
        "jsCode": "// Prepare final response payload\nconst ttsData = $('Post to Sarvam TTS').first().json;\nconst mergeData = $('Merge Response Streams').first().json;\n\n// TTS returns base64 audio chunks\nconst audioChunks = ttsData.audios || [];\nconst audioBase64 = audioChunks[0] || '';\n\nreturn {\n  json: {\n    success: true,\n    call_sid: mergeData.callSid,\n    caller_phone: mergeData.callerPhone,\n    transcribed_text: mergeData.transcribedText,\n    stt_failed: mergeData.sttFailed || false,\n    agent_response_text: mergeData.agentResponse,\n    audio_base64: audioBase64,\n    audio_format: 'wav',\n    sample_rate: 8000,\n    is_escalated: mergeData.isEscalated,\n    conversation_history: mergeData.conversationHistory,\n    continue_conversation: !mergeData.isEscalated,\n    timestamp: new Date().toISOString()\n  }\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "0672869d-7d19-412c-8b71-7dd675811723",
      "name": "Send Webhook Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1568,
        512
      ],
      "parameters": {
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              },
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{ $json }}"
      },
      "typeVersion": 1.1
    },
    {
      "id": "570904b0-0095-4230-b3df-b1c5b1889c83",
      "name": "Prepare Lead Log Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1536,
        832
      ],
      "parameters": {
        "jsCode": "const input = $input.first().json;\n\nconst history = Array.isArray(input.conversationHistory) ? input.conversationHistory : [];\nconst latestUserText = typeof input.transcribedText === 'string' ? input.transcribedText.trim() : '';\n\nconst isLikelyName = (text) => {\n  if (!text) return false;\n  const words = text.split(/\\s+/).filter(Boolean);\n  if (words.length === 0 || words.length > 3) return false;\n  if (/\\d/.test(text)) return false;\n  return text.length >= 2 && text.length <= 30;\n};\n\nconst askedNameRecently = history\n  .slice(-4)\n  .some((msg) => msg?.role === 'assistant' && typeof msg?.content === 'string' && msg.content.includes('\u0baa\u0bc6\u0baf\u0bb0\u0bcd'));\n\nconst callerName = askedNameRecently && isLikelyName(latestUserText) ? latestUserText : '';\n\nconst numberMatch = latestUserText.match(/(?:\\+?\\d[\\d\\s\\-]{7,}\\d)/);\nconst extractedCallerNumber = numberMatch ? numberMatch[0].replace(/\\s+/g, ' ').trim() : '';\n\nconst intentKeywords = [\n  { key: 'site_visit', words: ['\u0ba8\u0bc7\u0bb0\u0bbf\u0bb2\u0bcd', 'visit', '\u0baa\u0bbe\u0bb0\u0bcd\u0b95\u0bcd\u0b95'] },\n  { key: 'pricing', words: ['\u0bb5\u0bbf\u0bb2\u0bc8', 'price', '\u0bb2\u0b9f\u0bcd\u0b9a\u0bae\u0bcd', '\u0b95\u0bcb\u0b9f\u0bbf'] },\n  { key: 'location', words: ['area', '\u0b8f\u0bb0\u0bbf\u0baf\u0bbe', 'omr', 'porur', '\u0b85\u0ba3\u0bcd\u0ba3\u0bbe \u0ba8\u0b95\u0bb0\u0bcd', '\u0baa\u0bcb\u0bb0\u0bc2\u0bb0\u0bcd'] },\n  { key: 'callback', words: ['call', 'phone', '\u0ba4\u0bca\u0bb2\u0bc8\u0baa\u0bc7\u0b9a\u0bbf'] }\n];\n\nlet callerIntent = 'general_query';\nconst lowerText = latestUserText.toLowerCase();\nfor (const intent of intentKeywords) {\n  if (intent.words.some((w) => lowerText.includes(w.toLowerCase()))) {\n    callerIntent = intent.key;\n    break;\n  }\n}\n\nreturn [{\n  json: {\n    timestamp: new Date().toISOString(),\n    call_sid: input.callSid || '',\n    caller_id: input.callerPhone || '',\n    caller_phone: input.callerPhone || '',\n    caller_name: callerName,\n    caller_intent: callerIntent,\n    extracted_caller_number: extractedCallerNumber,\n    user_last_message: latestUserText,\n    agent_last_reply: input.agentResponse || '',\n    stt_failed: Boolean(input.sttFailed),\n    is_escalated: Boolean(input.isEscalated),\n    continue_conversation: !Boolean(input.isEscalated),\n    audio_format: '',\n    sample_rate: '',\n    history_length: history.length\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "352a19de-7824-47a8-aa70-79df92b2b918",
      "name": "Append Log to GSheets",
      "type": "n8n-nodes-base.googleSheets",
      "onError": "continueRegularOutput",
      "position": [
        1776,
        832
      ],
      "parameters": {
        "columns": {
          "value": {},
          "schema": [],
          "mappingMode": "autoMapInputData",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {
          "useAppend": true,
          "cellFormat": "USER_ENTERED",
          "handlingExtraData": "insertInNewColumn"
        },
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "{{.GOOGLE_SHEETS_SHEET_ID_1451193517}}"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "{{.GOOGLE_SHEETS_DOCUMENT_ID}}",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/14W9maZj3mvOuuYsbLVe1cDRQog9AeforKUuPvE_aq-4/edit?usp=drivesdk",
          "cachedResultName": "Voice AI"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    }
  ],
  "connections": {
    "If First Turn": {
      "main": [
        [
          {
            "node": "Generate Welcome Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Post to Sarvam STT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Handle Escalation": {
      "main": [
        [
          {
            "node": "Merge Response Streams",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to Sarvam STT": {
      "main": [
        [
          {
            "node": "Prepare STT Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Post to Sarvam TTS": {
      "main": [
        [
          {
            "node": "Build Response Payload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Webhook Input": {
      "main": [
        [
          {
            "node": "If First Turn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare STT Results": {
      "main": [
        [
          {
            "node": "Check for Escalation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for Escalation": {
      "main": [
        [
          {
            "node": "Handle Escalation",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "OpenAI Model Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Model Message": {
      "main": [
        [
          {
            "node": "Merge Response Streams",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Lead Log Data": {
      "main": [
        [
          {
            "node": "Append Log to GSheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Response Payload": {
      "main": [
        [
          {
            "node": "Send Webhook Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Response Streams": {
      "main": [
        [
          {
            "node": "Post to Sarvam TTS",
            "type": "main",
            "index": 0
          },
          {
            "node": "Prepare Lead Log Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Welcome Message": {
      "main": [
        [
          {
            "node": "Merge Response Streams",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When POST to Tamil Agent": {
      "main": [
        [
          {
            "node": "Parse Webhook Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}