{
  "name": "01 \u00b7 Lead Intake \u2192 AI Classification \u2192 Sheets + Telegram",
  "nodes": [
    {
      "parameters": {
        "content": "## 01 \u00b7 Lead intake with AI qualification\n**Webhook \u2192 normalize \u2192 GPT classification \u2192 Google Sheets \u2192 Telegram (hot leads only)**\n\nEvery lead is logged. Only leads the model marks `hot` interrupt anyone.\n\n**Before importing:** replace every `REPLACE_WITH_...` placeholder and\nattach your own credentials. See the README.",
        "height": 280,
        "width": 520,
        "color": 7
      },
      "id": "00000001-0000-4000-8000-000000000000",
      "name": "Overview",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -260,
        -200
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "lead-intake",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "00000001-0000-4000-8000-000000000001",
      "name": "Lead Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -240,
        120
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "f1",
              "name": "name",
              "value": "={{ ($json.body?.name ?? '').toString().trim() }}",
              "type": "string"
            },
            {
              "id": "f2",
              "name": "email",
              "value": "={{ ($json.body?.email ?? '').toString().trim().toLowerCase() }}",
              "type": "string"
            },
            {
              "id": "f3",
              "name": "company",
              "value": "={{ ($json.body?.company ?? '').toString().trim() }}",
              "type": "string"
            },
            {
              "id": "f4",
              "name": "message",
              "value": "={{ ($json.body?.message ?? '').toString().slice(0, 4000) }}",
              "type": "string"
            },
            {
              "id": "f5",
              "name": "source",
              "value": "={{ $json.body?.source ?? 'website' }}",
              "type": "string"
            },
            {
              "id": "f6",
              "name": "received_at",
              "value": "={{ $now.toISO() }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": false,
        "options": {}
      },
      "id": "00000001-0000-4000-8000-000000000002",
      "name": "Normalize Lead",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -20,
        120
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"gpt-4o-mini\",\n  \"temperature\": 0,\n  \"response_format\": { \"type\": \"json_object\" },\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You qualify inbound B2B leads. Reply with JSON only, using exactly these keys: priority (hot|warm|cold), intent (new_project|hiring|partnership|support|spam), budget_signal (explicit|implied|none), summary (max 200 characters, English), next_action (max 120 characters), confidence (0-1). Mark priority=hot only when the lead names a concrete project AND some budget or timeline signal. Anything automated, promotional or unrelated is intent=spam and priority=cold.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(`Name: ${$json.name}\\nEmail: ${$json.email}\\nCompany: ${$json.company}\\nSource: ${$json.source}\\nMessage: ${$json.message}`) }}\n    }\n  ]\n}",
        "options": {
          "timeout": 60000
        }
      },
      "id": "00000001-0000-4000-8000-000000000003",
      "name": "Classify Lead",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        200,
        120
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 2000
    },
    {
      "parameters": {
        "jsCode": "// The model answers with a JSON string inside choices[0].message.content.\n// Never trust it blindly: a parse failure must not kill the run.\nconst lead = $('Normalize Lead').first().json;\nconst raw = $json.choices?.[0]?.message?.content ?? '{}';\n\nlet parsed;\ntry {\n  parsed = JSON.parse(raw);\n} catch (error) {\n  parsed = {\n    priority: 'warm',\n    intent: 'unknown',\n    summary: String(raw).slice(0, 200),\n    next_action: 'Review manually - the classifier returned malformed JSON.',\n    confidence: 0,\n  };\n}\n\nconst allowed = ['hot', 'warm', 'cold'];\nconst priority = String(parsed.priority ?? '').toLowerCase();\n\nreturn [\n  {\n    json: {\n      ...lead,\n      priority: allowed.includes(priority) ? priority : 'warm',\n      intent: parsed.intent ?? 'unknown',\n      budget_signal: parsed.budget_signal ?? 'none',\n      summary: parsed.summary ?? '',\n      next_action: parsed.next_action ?? '',\n      confidence: Number(parsed.confidence ?? 0),\n      classified_at: new Date().toISOString(),\n    },\n  },\n];"
      },
      "id": "00000001-0000-4000-8000-000000000004",
      "name": "Parse Classification",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        420,
        120
      ]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "REPLACE_WITH_GOOGLE_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "Leads",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Received At": "={{ $json.received_at }}",
            "Name": "={{ $json.name }}",
            "Email": "={{ $json.email }}",
            "Company": "={{ $json.company }}",
            "Source": "={{ $json.source }}",
            "Priority": "={{ $json.priority }}",
            "Intent": "={{ $json.intent }}",
            "Budget Signal": "={{ $json.budget_signal }}",
            "Summary": "={{ $json.summary }}",
            "Next Action": "={{ $json.next_action }}",
            "Confidence": "={{ $json.confidence }}"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "id": "00000001-0000-4000-8000-000000000005",
      "name": "Log to Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        640,
        120
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 2000
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.priority }}",
              "rightValue": "hot",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "c2",
              "leftValue": "={{ $json.confidence }}",
              "rightValue": 0.6,
              "operator": {
                "type": "number",
                "operation": "gte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "00000001-0000-4000-8000-000000000006",
      "name": "Is Hot Lead?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        860,
        120
      ]
    },
    {
      "parameters": {
        "chatId": "REPLACE_WITH_TELEGRAM_CHAT_ID",
        "text": "=\ud83d\udd25 <b>Hot lead</b>\n\n<b>{{ $json.name }}</b> \u2014 {{ $json.company || 'no company' }}\n{{ $json.email }}\n\n<b>Intent:</b> {{ $json.intent }}\n<b>Budget:</b> {{ $json.budget_signal }}\n<b>Confidence:</b> {{ $json.confidence }}\n\n{{ $json.summary }}\n\n<b>Next:</b> {{ $json.next_action }}",
        "additionalFields": {
          "parse_mode": "HTML"
        }
      },
      "id": "00000001-0000-4000-8000-000000000007",
      "name": "Alert Sales on Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1080,
        20
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {},
      "id": "00000001-0000-4000-8000-000000000008",
      "name": "Logged Only",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1080,
        220
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\": \"received\", \"priority\": \"{{ $json.priority }}\"}",
        "options": {}
      },
      "id": "00000001-0000-4000-8000-000000000009",
      "name": "Respond to Sender",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1300,
        120
      ]
    }
  ],
  "connections": {
    "Lead Webhook": {
      "main": [
        [
          {
            "node": "Normalize Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Lead": {
      "main": [
        [
          {
            "node": "Classify Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Lead": {
      "main": [
        [
          {
            "node": "Parse Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Classification": {
      "main": [
        [
          {
            "node": "Log to Google Sheets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Google Sheets": {
      "main": [
        [
          {
            "node": "Is Hot Lead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Hot Lead?": {
      "main": [
        [
          {
            "node": "Alert Sales on Telegram",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Logged Only",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Alert Sales on Telegram": {
      "main": [
        [
          {
            "node": "Respond to Sender",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Logged Only": {
      "main": [
        [
          {
            "node": "Respond to Sender",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveManualExecutions": true
  },
  "tags": []
}