{
  "name": "Chat drafting with intent routing — 1 AI A Day (Day 08)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "new-chat",
        "options": {}
      },
      "id": "a4db4611-f2f9-42c3-9e4f-3ed62ff35012",
      "name": "New chat message",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "8f559409-e987-4846-8cc5-eb5a8d85466b"
    },
    {
      "parameters": {
        "jsCode": "// ---- Paste your voice file here -----------------------------------------\nconst MODEL = 'REPLACE-WITH-MODEL-NAME';   // the Claude model you want to use\n\nconst STYLE_RULES = `\n1. Greeting: \"Hi!\" — never \"Dear customer\".\n2. Length: one to three short lines.\n3. Formality: casual English, mixes in Malay naturally (\"boleh\", \"ya\").\n4. Emoji: at most one, usually 👍 or 🙏, at the end.\n5. Good news: say it first, then the detail.\n6. Bad news: say sorry once, then what happens next. No excuses.\n7. Sign-off: none, or \"any other size you need let me know\".\n8. Always gives a concrete time or number when one exists.\n`;\n\nconst EXAMPLES = `\nPRESALE\nBuyer: boleh muat laptop 15 inch?\nMe: Yes boleh! Fits up to 16 inch. Padded base so the laptop is safe 👍\n\nSTATUS\nBuyer: when will ship?\nMe: Hi! Order in before 3pm ships same day, after 3pm ships tomorrow.\n`;\n// --------------------------------------------------------------------------\n\nconst msg = $input.first().json.body ?? $input.first().json;\nreturn [{ json: {\n  model: MODEL,\n  conversation_id: msg.conversation_id,\n  channel: msg.channel,\n  buyer_message: msg.buyer_message,\n  style_rules: STYLE_RULES,\n  examples: EXAMPLES,\n}}];\n"
      },
      "id": "c90b9263-c238-4d09-b979-6867ef997cec",
      "name": "Voice file",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: $(\"Voice file\").first().json.model, max_tokens: 400, messages: [{ role: \"user\", content: \"Classify this customer message into exactly one label: PRESALE, ORDER_STATUS, COMPLAINT, SPAM, UNSURE.\\n\\nPRESALE: a question from someone considering buying. ORDER_STATUS: a question about an existing order with no problem stated. COMPLAINT: anything suggesting dissatisfaction, damage, a wrong or missing item, lateness, or a request to return or refund. SPAM: promotion or bots. UNSURE: anything else, or when not confident.\\n\\nRules in priority order: 1) Any hint of a problem - including rosak, pecah, salah, tak sampai, lambat, tipu, refund, pulangkan, broken, wrong, missing, late - is COMPLAINT even if it also asks something else. 2) If not confident, UNSURE. 3) Bare greetings are UNSURE.\\n\\nReply with the label only.\\n\\nMESSAGE:\\n\" + $(\"Voice file\").first().json.buyer_message }] }) }}",
        "options": {}
      },
      "id": "2629657f-bc25-4303-9d30-e07e86d856dc",
      "name": "Classify intent",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        0
      ],
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "jsCode": "// Complaint beats everything. Anything unrecognised goes to a human.\nconst LABELS = ['PRESALE', 'ORDER_STATUS', 'COMPLAINT', 'SPAM', 'UNSURE'];\nconst raw = String($json.content?.[0]?.text ?? '').toUpperCase();\nconst label = LABELS.find(l => raw.includes(l)) ?? 'UNSURE';\nconst route = raw.includes('COMPLAINT') ? 'human'\n            : label === 'SPAM' ? 'drop'\n            : (label === 'PRESALE' || label === 'ORDER_STATUS') ? 'draft'\n            : 'human';\nconst v = $('Voice file').first().json;\nreturn [{ json: { label, route, buyer_message: v.buyer_message,\n                  conversation_id: v.conversation_id, channel: v.channel } }];\n"
      },
      "id": "a3aca476-8cc3-4988-96d0-73769a24ac82",
      "name": "Apply routing rules",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        0
      ]
    },
    {
      "parameters": {
        "mode": "expression",
        "numberOutputs": 3,
        "output": "={{ ({ draft: 0, human: 1, drop: 2 })[$json.route] ?? 1 }}"
      },
      "id": "f4ad9b91-a2a2-463b-a14a-f6c58ac85871",
      "name": "Route",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: $(\"Voice file\").first().json.model, max_tokens: 400, messages: [{ role: \"user\", content: \"You draft customer replies for my marketplace shop, in my voice.\\n\\nMY STYLE RULES:\\n{{ $('Voice file').first().json.style_rules }}\\n\\nEXAMPLES OF MY REPLIES:\\n{{ $('Voice file').first().json.examples }}\\n\\nNEW MESSAGE FROM A BUYER:\\n{{ $('Voice file').first().json.buyer_message }}\\n\\nDraft one reply following my rules. If answering needs a fact you don't have - stock, a dispatch date, a spec - write [CHECK: what's needed] instead of guessing. Never promise a refund, replacement, discount or delivery date. Return the draft only.\" }] }) }}",
        "options": {}
      },
      "id": "6ef3992a-e702-49d2-b37c-1b3678cb5e37",
      "name": "Draft reply in your voice",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        -120
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://REPLACE-WITH-YOUR-MESSAGING-ENDPOINT/send",
        "sendBody": true,
        "specifyBody": "keypair",
        "bodyParameters": {
          "parameters": [
            {
              "name": "to",
              "value": "REPLACE-WITH-YOUR-OWN-NUMBER"
            },
            {
              "name": "text",
              "value": "=DRAFT ({{ $('Apply routing rules').first().json.label }}) — {{ $('Apply routing rules').first().json.channel }}\nBuyer: {{ $('Apply routing rules').first().json.buyer_message }}\n\nSuggested reply:\n{{ $json.content[0].text }}\n\nNothing has been sent. Check any [CHECK] items, then send it yourself."
            }
          ]
        },
        "options": {}
      },
      "id": "0caeface-45ad-4b02-a756-3f2311b7a70a",
      "name": "Draft → you for approval",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1320,
        -120
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://REPLACE-WITH-YOUR-MESSAGING-ENDPOINT/send",
        "sendBody": true,
        "specifyBody": "keypair",
        "bodyParameters": {
          "parameters": [
            {
              "name": "to",
              "value": "REPLACE-WITH-YOUR-OWN-NUMBER"
            },
            {
              "name": "text",
              "value": "=NEEDS YOU — {{ $json.label ?? 'CLASSIFIER FAILED' }} — {{ $json.channel ?? '' }}\nBuyer: {{ $json.buyer_message ?? $('Voice file').first().json.buyer_message }}\n\nNo draft. Reply personally."
            }
          ]
        },
        "options": {}
      },
      "id": "0e3c212f-5bed-44fc-b676-3aa738ecd0da",
      "name": "Needs a human → you",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1100,
        120
      ]
    },
    {
      "parameters": {},
      "id": "f2600f34-4ca4-43c2-bf17-e2c9428b137d",
      "name": "Spam — drop",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        1100,
        320
      ]
    }
  ],
  "connections": {
    "New chat message": {
      "main": [
        [
          {
            "node": "Voice file",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Voice file": {
      "main": [
        [
          {
            "node": "Classify intent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify intent": {
      "main": [
        [
          {
            "node": "Apply routing rules",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Needs a human → you",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Apply routing rules": {
      "main": [
        [
          {
            "node": "Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route": {
      "main": [
        [
          {
            "node": "Draft reply in your voice",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Needs a human → you",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Spam — drop",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Draft reply in your voice": {
      "main": [
        [
          {
            "node": "Draft → you for approval",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {
    "New chat message": [
      {
        "json": {
          "headers": {},
          "params": {},
          "query": {},
          "body": {
            "conversation_id": "[CONVERSATION]",
            "channel": "Shopee",
            "buyer_message": "boleh muat laptop 15 inch? ada stock navy lagi?"
          }
        }
      }
    ]
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false,
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}