{
  "id": "mQeUGiy3jYf6liN5",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Customer 360 - Help Scout, HubSpot & SMS",
  "tags": [],
  "nodes": [
    {
      "id": "22e2a6c9-34ad-444a-b84d-0f09f40a04cc",
      "name": "Sticky Note Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -80,
        320
      ],
      "parameters": {
        "color": 4,
        "width": 608,
        "height": 656,
        "content": "## AI Customer 360 (Help Scout + HubSpot + SMS)\n\n### How it works\n1. Receives Help Scout events via webhook  \n2. Extracts customer and ticket data  \n3. Fetches HubSpot CRM data and recent SMS history  \n4. Merges all data into a unified customer context  \n5. Uses AI to generate a draft reply and perform QA (sentiment + approval)  \n6. Routes the ticket:\n   - Negative/Angry \u2192 Escalate to human  \n   - Approved + High value \u2192 Save draft  \n   - Others \u2192 Manual review  \n\n---\n\n### Setup\n- Configure Help Scout webhook (POST \u2192 this workflow)  \n- Add HubSpot API credentials  \n- Add SMS API credentials  \n- Add OpenAI API credentials  \n\n**Required variables:**\n- `salesMessengerApiUrl` = SMS API endpoint  \n- `highValueThreshold` = deal value threshold  \n- `humanAgentId` = Help Scout user ID for escalation  \n\n---\n\n### Customization\n- Adjust sentiment routing logic in the Switch node  \n- Modify AI prompts for tone, brand voice, or constraints  \n- Change approval rules (deal value threshold, QA conditions)  \n- Extend data sources (e.g., add more CRM fields or channels)  "
      },
      "typeVersion": 1
    },
    {
      "id": "8ddf88ca-671b-40df-977c-2af423529d0d",
      "name": "HelpScout Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        848,
        672
      ],
      "parameters": {
        "path": "helpscout-context-sync",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 1
    },
    {
      "id": "4b4d3c7a-b89c-497b-87ca-cad26f4d381d",
      "name": "Parse Event",
      "type": "n8n-nodes-base.code",
      "position": [
        1088,
        672
      ],
      "parameters": {
        "jsCode": "// Catch HelpScout webhook and route based on X-HelpScout-Event header\nconst headers = $input.first().json.headers;\nconst body = $input.first().json.body;\nconst eventType = headers['x-helpscout-event'];\n\n// Only process new conversations or customer replies\nif (eventType === 'convo.created' || eventType === 'convo.customer.reply.created') {\n  return [{ json: { eventType, payload: body } }];\n}\n\n// Ignore other events to save API calls\nreturn [];"
      },
      "typeVersion": 2
    },
    {
      "id": "6f13325e-53ab-4fa0-93b2-0cf24d88aa71",
      "name": "Extract Data",
      "type": "n8n-nodes-base.code",
      "position": [
        1328,
        672
      ],
      "parameters": {
        "jsCode": "// Extract core identifiers from HelpScout v2 payload\nconst payload = $json.payload;\nconst email = payload.primaryCustomer?.emails?.[0]?.value || payload.customer?.email;\nconst ticketId = payload.id;\nconst subject = payload.subject;\nconst preview = payload.preview;\n\nif (!email) return []; // Drop if no email\n\nreturn [{ json: { email, ticketId, subject, preview, source: 'helpscout' } }];"
      },
      "typeVersion": 2
    },
    {
      "id": "122f7883-fe6e-4d08-a4e9-7d81b0ac618f",
      "name": "Fetch HubSpot Context",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1568,
        592
      ],
      "parameters": {
        "url": "https://api.hubapi.com/crm/v3/objects/contacts/search",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"filterGroups\": [\n    {\n      \"filters\": [\n        {\n          \"propertyName\": \"email\",\n          \"operator\": \"EQ\",\n          \"value\": \"{{ $json.email }}\"\n        }\n      ]\n    }\n  ],\n  \"properties\": [\"dealstage\", \"account_tier\", \"onboarding_status\", \"associated_deal_amount\"]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "hubSpotApi"
      },
      "typeVersion": 4.1
    },
    {
      "id": "f982e35b-7ebe-4c3c-80df-77e916797532",
      "name": "Fetch SMS History",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1568,
        752
      ],
      "parameters": {
        "url": "={{ $vars.salesMessengerApiUrl }}?contact_email={{ $json.email }}&limit=5",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "salesMessengerApi"
      },
      "typeVersion": 4.1
    },
    {
      "id": "b83dc7d3-3796-412a-ab01-6b651f126873",
      "name": "Merge Context",
      "type": "n8n-nodes-base.merge",
      "position": [
        1888,
        672
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combinationMode": "mergeByPosition"
      },
      "typeVersion": 2
    },
    {
      "id": "e1d2def8-77fa-4ec1-ab57-7caaeef10d19",
      "name": "Build Shared Context Layer",
      "type": "n8n-nodes-base.code",
      "position": [
        2048,
        672
      ],
      "parameters": {
        "jsCode": "const hsData = $json[0]?.results?.[0] || {};\nconst smsData = $json[1]?.messages || [];\nconst inputData = $json[0]?.inputData || {};\n\nlet smsHistoryString = \"No recent SMS.\";\nif (smsData.length > 0) {\n  smsHistoryString = smsData.map(m => `[${m.direction}] ${m.body}`).join(' | ');\n}\n\nconst sharedContext = `\n--- CUSTOMER 360 CONTEXT ---\n1. CURRENT TICKET (Help Scout): ID {{ $json[\"ticketId\"] }}, Subject: {{ $json[\"subject\"] }}, Preview: {{ $json[\"preview\"] }}\n2. HUBSPOT CRM: Deal Stage: ${hsData.dealstage || 'N/A'}, Account Tier: ${hsData.account_tier || 'Free'}, Onboarding Status: ${hsData.onboarding_status || 'N/A'}, Associated Deal Value: ${hsData.associated_deal_amount || 0}\n3. RECENT SMS ACTIVITY: ${smsHistoryString}\n---------------------------\n`;\n\nreturn [{ json: { \n  sharedContext, \n  email: inputData.email, \n  ticketId: inputData.ticketId,\n  accountTier: hsData.account_tier || 'Free',\n  dealValue: Number(hsData.associated_deal_amount) || 0\n}}];"
      },
      "typeVersion": 2
    },
    {
      "id": "56cfd2b6-b349-4f53-bbe0-614ab1a8ce61",
      "name": "AI Draft Generator",
      "type": "n8n-nodes-base.openAi",
      "position": [
        2384,
        672
      ],
      "parameters": {
        "model": "gpt-4o",
        "prompt": "You are an empathetic support agent for a K-12 SaaS. Using ONLY the provided context, draft a reply. Do not invent features.\n\n{{ $json.sharedContext }}\n\nDRAFTED REPLY:",
        "options": {},
        "requestOptions": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e9d76a0a-e003-4bff-94ef-dccc9f306c76",
      "name": "AI QA & Sentiment Check",
      "type": "n8n-nodes-base.openAi",
      "position": [
        2560,
        672
      ],
      "parameters": {
        "model": "gpt-4o-mini",
        "prompt": "You are a QA auditor. Review this AI drafted response for a K-12 SaaS. Check for brand safety and empathy. \n\nRespond ONLY in JSON format:\n{ \"approved\": boolean, \"sentiment_score\": \"positive/neutral/negative/angry\", \"reason\": \"string\" }\n\nDRAFT TO EVALUATE:\n{{ $json.message.content }}",
        "options": {},
        "requestOptions": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c30dc6f2-ee33-4b1d-ac3d-8f29820181b3",
      "name": "Parse QA Output",
      "type": "n8n-nodes-base.code",
      "position": [
        2864,
        672
      ],
      "parameters": {
        "jsCode": "const qaResult = JSON.parse($json.message.content);\nconst draft = $('AI Draft Generator').item.json.message.content;\n\nreturn [{ \n  json: {\n    ...qaResult,\n    draft,\n    email: $('Build Shared Context Layer').item.json.email,\n    ticketId: $('Build Shared Context Layer').item.json.ticketId,\n    dealValue: $('Build Shared Context Layer').item.json.dealValue\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "cfe03de4-1069-4b00-af3a-5c06689e8369",
      "name": "Sentiment Router",
      "type": "n8n-nodes-base.switch",
      "position": [
        3008,
        672
      ],
      "parameters": {
        "rules": {
          "rules": [
            {
              "value2": "angry",
              "outputKey": "Angry Sentiment"
            },
            {
              "value2": "negative",
              "outputKey": "Negative Sentiment"
            }
          ]
        },
        "value1": "={{ $json.sentiment_score }}",
        "dataType": "string",
        "fallbackOutput": 1
      },
      "typeVersion": 2
    },
    {
      "id": "84d3d39e-37fe-45cb-bd7a-427d7d2c93bf",
      "name": "High Value + Approved?",
      "type": "n8n-nodes-base.if",
      "position": [
        3248,
        592
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "check_approved",
              "operator": {
                "type": "boolean",
                "operation": "true"
              },
              "leftValue": "={{ $json.approved }}"
            },
            {
              "id": "check_value",
              "operator": {
                "type": "number",
                "operation": "largerEqual"
              },
              "leftValue": "={{ $json.dealValue }}",
              "rightValue": "={{ $vars.highValueThreshold }}"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "5b1f8c4c-9b72-46d7-a080-0b30b5de7dee",
      "name": "Save Draft (Auto-Suggest)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3488,
        512
      ],
      "parameters": {
        "url": "https://api.helpscout.net/v2/conversations/{{ $json.ticketId }}/threads",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"type\": \"reply\",\n  \"text\": \"{{ $json.draft }}\",\n  \"draft\": true,\n  \"createdBy\": { \"type\": \"bot\", \"id\": \"123\" }\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "helpScoutApi"
      },
      "typeVersion": 4.1
    },
    {
      "id": "ee685206-ceec-4b8f-88c3-c38ab5f8bb56",
      "name": "Escalate to Human",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3488,
        752
      ],
      "parameters": {
        "url": "https://api.helpscout.net/v2/conversations/{{ $json.ticketId }}",
        "method": "PATCH",
        "options": {},
        "jsonBody": "={\n  \"assignedTo\": { \"type\": \"user\", \"id\": \"{{ $vars.humanAgentId }}\" },\n  \"tags\": [\"ai-escalation\", \"{{ $json.sentiment_score }}\"]\n}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "helpScoutApi"
      },
      "typeVersion": 4.1
    },
    {
      "id": "001504db-cc47-486f-8cda-466369782345",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        3728,
        592
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={ \"status\": \"processed_and_routed\" }"
      },
      "typeVersion": 1
    },
    {
      "id": "89f1a66f-617d-40f9-a8bf-938b95a8071e",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        816,
        480
      ],
      "parameters": {
        "width": 384,
        "height": 352,
        "content": "## 1: Ingestion\n\n### Webhook Trigger and Parse\n\nReceives HelpScout events through a webhook and parses them for routing."
      },
      "typeVersion": 1
    },
    {
      "id": "f4f51a94-1c2e-4209-81f9-891f09c3641b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1328,
        464
      ],
      "parameters": {
        "width": 416,
        "height": 448,
        "content": "## 2: Data Enrichment\n\n### Extract and Fetch Data\n\nExtracts identifiers and fetches HubSpot and SMS histories for context."
      },
      "typeVersion": 1
    },
    {
      "id": "9f12902b-00f6-4cc4-a74b-4edd70ebcc47",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1840,
        496
      ],
      "parameters": {
        "width": 352,
        "height": 352,
        "content": "## 3: Context Building\n\n### Merge and Build Context\n\nCombines fetched data and builds a shared context layer."
      },
      "typeVersion": 1
    },
    {
      "id": "4ed9d98b-e872-4837-a47a-e1d7c52a0596",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2336,
        480
      ],
      "parameters": {
        "width": 368,
        "height": 384,
        "content": "##  4: AI Processing\n\n### AI Processing and QA\n\nGenerates AI drafts and performs quality and sentiment checks.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "138122c9-6e67-4781-acc7-5e5ca091c30e",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2832,
        320
      ],
      "parameters": {
        "width": 848,
        "height": 640,
        "content": "## Group 5: Routing & Action\n\n### Sentiment Routing and Action\n\nRoutes based on sentiment analysis and determines if escalation is needed."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "c8798a24-be0d-4891-93ab-acbefa901064",
  "connections": {
    "Parse Event": {
      "main": [
        [
          {
            "node": "Extract Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Data": {
      "main": [
        [
          {
            "node": "Fetch HubSpot Context",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch SMS History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Context": {
      "main": [
        [
          {
            "node": "Build Shared Context Layer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse QA Output": {
      "main": [
        [
          {
            "node": "Sentiment Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sentiment Router": {
      "main": [
        [
          {
            "node": "Escalate to Human",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "High Value + Approved?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Escalate to Human": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch SMS History": {
      "main": [
        [
          {
            "node": "Merge Context",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "HelpScout Webhook": {
      "main": [
        [
          {
            "node": "Parse Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Draft Generator": {
      "main": [
        [
          {
            "node": "AI QA & Sentiment Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch HubSpot Context": {
      "main": [
        [
          {
            "node": "Merge Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "High Value + Approved?": {
      "main": [
        [
          {
            "node": "Save Draft (Auto-Suggest)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Escalate to Human",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI QA & Sentiment Check": {
      "main": [
        [
          {
            "node": "Parse QA Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Draft (Auto-Suggest)": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Shared Context Layer": {
      "main": [
        [
          {
            "node": "AI Draft Generator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}