{
  "name": "Automated Chat & Text Responses",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "chat-intake",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-intake",
      "name": "Incoming Message",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [240, 300],
      "webhookId": "chat-intake"
    },
    {
      "parameters": {
        "jsCode": "const body = $input.first().json.body || $input.first().json;\nconst message = body.message || body.text || body.Body || '';\nconst sender = body.sender || body.from || body.From || '';\nconst channel = body.channel || 'web';\n\nif (!message.trim()) {\n  return [{\n    json: {\n      skip: true,\n      reply: 'Sorry, I did not receive a message. Could you try again?',\n      escalated: false,\n      category: 'empty'\n    }\n  }];\n}\n\nconst businessContext = process.env.BUSINESS_CONTEXT || 'a professional services company';\n\nconst requestBody = {\n  model: 'claude-sonnet-4-20250514',\n  max_tokens: 1024,\n  system: 'You are a customer support assistant for ' + businessContext + '. Answer questions about the business directly and helpfully. Keep replies concise (under 80 words). Be warm and professional.\\n\\nIMPORTANT RULES:\\n- If you do not know the answer, say so honestly and offer to connect them with a human\\n- NEVER make up pricing, policies, or features you are not sure about\\n- Set needs_human to true for: billing disputes, refund requests, complaints, technical bugs, anything you are unsure about\\n- Set confidence to how sure you are (0.0 to 1.0)\\n\\nRespond with JSON only:\\n{\"needs_human\": true/false, \"confidence\": 0.0-1.0, \"reply\": \"your response\", \"category\": \"general\" | \"pricing\" | \"support\" | \"billing\" | \"complaint\" | \"hours\" | \"other\"}',\n  messages: [\n    { role: 'user', content: message }\n  ]\n};\n\nreturn [{\n  json: {\n    requestBody,\n    message,\n    sender,\n    channel,\n    timestamp: new Date().toISOString(),\n    skip: false\n  }\n}];"
      },
      "id": "build-request",
      "name": "Build Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [460, 300]
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true },
          "conditions": [
            {
              "leftValue": "={{ $json.skip }}",
              "rightValue": true,
              "operator": { "type": "boolean", "operation": "notEquals" }
            }
          ]
        }
      },
      "id": "check-skip",
      "name": "Has Message?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [680, 300]
    },
    {
      "parameters": {
        "url": "https://api.anthropic.com/v1/messages",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            { "name": "x-api-key", "value": "={{ $env.ANTHROPIC_API_KEY }}" },
            { "name": "anthropic-version", "value": "2023-06-01" },
            { "name": "content-type", "value": "application/json" }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.requestBody) }}",
        "options": {}
      },
      "id": "call-claude",
      "name": "Generate Response",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [900, 240]
    },
    {
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst content = response.content[0].text;\nconst prev = $('Build Request').first().json;\n\nlet parsed;\ntry {\n  const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n  parsed = JSON.parse(jsonMatch[0]);\n} catch (e) {\n  parsed = {\n    needs_human: true,\n    confidence: 0,\n    reply: 'I am having trouble processing your request. Let me connect you with someone who can help.',\n    category: 'other'\n  };\n}\n\nconst shouldEscalate = parsed.needs_human || parsed.confidence < 0.7;\n\nreturn [{\n  json: {\n    reply: parsed.reply,\n    category: parsed.category,\n    confidence: parsed.confidence,\n    escalated: shouldEscalate,\n    message: prev.message,\n    sender: prev.sender,\n    channel: prev.channel\n  }\n}];"
      },
      "id": "parse-response",
      "name": "Parse Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1120, 240]
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true },
          "conditions": [
            {
              "leftValue": "={{ $json.escalated }}",
              "rightValue": true,
              "operator": { "type": "boolean", "operation": "equals" }
            }
          ]
        }
      },
      "id": "check-escalation",
      "name": "Needs Human?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [1340, 240]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first().json;\nreturn [{ json: { text: 'Customer needs help (confidence: ' + item.confidence + ')\\nFrom: ' + item.sender + ' (' + item.channel + ')\\nCategory: ' + item.category + '\\nMessage: ' + item.message + '\\nAI reply sent: ' + item.reply } }];"
      },
      "id": "build-slack",
      "name": "Build Slack Alert",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1560, 140]
    },
    {
      "parameters": {
        "url": "={{ $env.SLACK_WEBHOOK_URL }}",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "id": "notify-team",
      "name": "Notify Team",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1780, 140]
    },
    {
      "parameters": {
        "jsCode": "const item = $('Parse Response').first().json;\nreturn [{ json: { reply: item.reply, escalated: true, category: item.category, confidence: item.confidence } }];"
      },
      "id": "build-escalate-resp",
      "name": "Build Escalated Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [2000, 140]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "id": "respond-escalate",
      "name": "Reply + Escalate",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [2220, 140]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first().json;\nreturn [{ json: { reply: item.reply, escalated: false, category: item.category, confidence: item.confidence } }];"
      },
      "id": "build-auto-resp",
      "name": "Build Auto Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1560, 380]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "id": "respond-auto",
      "name": "Auto Reply",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1780, 380]
    },
    {
      "parameters": {
        "jsCode": "const item = $input.first().json;\nreturn [{ json: { reply: item.reply, escalated: false, category: 'empty', confidence: 1 } }];"
      },
      "id": "build-empty-resp",
      "name": "Build Empty Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [900, 400]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {}
      },
      "id": "respond-empty",
      "name": "Reply Empty",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [1120, 400]
    }
  ],
  "connections": {
    "Incoming Message": { "main": [[{ "node": "Build Request", "type": "main", "index": 0 }]] },
    "Build Request": { "main": [[{ "node": "Has Message?", "type": "main", "index": 0 }]] },
    "Has Message?": {
      "main": [
        [{ "node": "Generate Response", "type": "main", "index": 0 }],
        [{ "node": "Build Empty Response", "type": "main", "index": 0 }]
      ]
    },
    "Generate Response": { "main": [[{ "node": "Parse Response", "type": "main", "index": 0 }]] },
    "Parse Response": { "main": [[{ "node": "Needs Human?", "type": "main", "index": 0 }]] },
    "Needs Human?": {
      "main": [
        [{ "node": "Build Slack Alert", "type": "main", "index": 0 }],
        [{ "node": "Build Auto Response", "type": "main", "index": 0 }]
      ]
    },
    "Build Slack Alert": { "main": [[{ "node": "Notify Team", "type": "main", "index": 0 }]] },
    "Notify Team": { "main": [[{ "node": "Build Escalated Response", "type": "main", "index": 0 }]] },
    "Build Escalated Response": { "main": [[{ "node": "Reply + Escalate", "type": "main", "index": 0 }]] },
    "Build Auto Response": { "main": [[{ "node": "Auto Reply", "type": "main", "index": 0 }]] },
    "Build Empty Response": { "main": [[{ "node": "Reply Empty", "type": "main", "index": 0 }]] }
  },
  "settings": { "executionOrder": "v1" },
  "meta": { "templateCredsSetupCompleted": false, "instanceId": "loomiq-template" },
  "tags": ["loomiq", "chat", "ai"]
}
