{
  "id": "port09GroundedSupportOperations",
  "name": "Portfolio 09 - Grounded AI Support Operations",
  "active": false,
  "nodes": [
    {
      "parameters": { "httpMethod": "POST", "path": "portfolio-grounded-support", "responseMode": "responseNode", "options": {} },
      "webhookId": "b9010000-0000-4000-8000-000000000098",
      "id": "b9010000-0000-4000-8000-000000000001",
      "name": "Support Ticket Event",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [-1080, 0]
    },
    {
      "parameters": {
        "jsCode": "const input = $json.body ?? $json;\nconst required = ['providerEventId', 'ticketId', 'customerEmail', 'question'];\nconst missing = required.filter((field) => input[field] === undefined || input[field] === null || input[field] === '');\nif (missing.length) return [{ json: { valid: false, error: `Missing required support field: ${missing.join(', ')}` } }];\nconst question = String(input.question).trim();\nif (question.length < 8) return [{ json: { valid: false, error: 'Support question is too short' } }];\nconst providerEventId = String(input.providerEventId).trim();\nconst injectionPattern = /(ignore|disregard).{0,20}(previous|system|instructions)|reveal.{0,20}(prompt|secret)|developer message|system prompt/i;\nconst sensitivePattern = /change (my )?(email|identity)|account takeover|bank account|credit card|legal threat|delete (my )?account/i;\nlet handoffReason = null;\nif (injectionPattern.test(question)) handoffReason = 'unsafe-instruction-pattern';\nelse if (sensitivePattern.test(question) || String(input.riskLevel || '').toLowerCase() === 'high') handoffReason = 'sensitive-account-request';\nreturn [{ json: { valid: true, providerEventId, ticketId: String(input.ticketId).trim(), customerEmail: String(input.customerEmail).trim().toLowerCase(), question, channel: String(input.channel || 'email').toLowerCase(), language: String(input.language || 'en'), handoffReason, idempotencyKey: `grounded-support:${providerEventId}`, simulateKnowledgeFailure: input.simulateKnowledgeFailure === true, simulateTicketingFailure: input.simulateTicketingFailure === true } }];"
      },
      "id": "b9010000-0000-4000-8000-000000000002",
      "name": "Validate and Guard Ticket",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [-860, 0]
    },
    {
      "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "conditions": [{ "id": "b9010000-0000-4000-8000-000000000003", "leftValue": "={{ $json.valid }}", "rightValue": true, "operator": { "type": "boolean", "operation": "true", "singleValue": true } }], "combinator": "and" }, "options": {} },
      "id": "b9010000-0000-4000-8000-000000000004",
      "name": "Valid Ticket?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [-640, 0]
    },
    {
      "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({ ok: false, status: 'invalid-ticket', error: $json.error }) }}", "options": { "responseCode": 400 } },
      "id": "b9010000-0000-4000-8000-000000000005",
      "name": "Return Invalid Ticket",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [-400, 180]
    },
    {
      "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "conditions": [{ "id": "b9010000-0000-4000-8000-000000000006", "leftValue": "={{ Boolean($json.handoffReason) }}", "rightValue": true, "operator": { "type": "boolean", "operation": "true", "singleValue": true } }], "combinator": "and" }, "options": {} },
      "id": "b9010000-0000-4000-8000-000000000007",
      "name": "Immediate Handoff Required?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [-400, -40]
    },
    {
      "parameters": { "method": "POST", "url": "http://127.0.0.1:8787/grounded-support/search", "sendBody": true, "contentType": "raw", "rawContentType": "application/json", "body": "={{ JSON.stringify($json) }}", "options": { "timeout": 10000 } },
      "id": "b9010000-0000-4000-8000-000000000008",
      "name": "Search Verified Knowledge",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [-160, -140],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "conditions": [{ "id": "b9010000-0000-4000-8000-000000000009", "leftValue": "={{ $json.confidence !== undefined }}", "rightValue": true, "operator": { "type": "boolean", "operation": "true", "singleValue": true } }], "combinator": "and" }, "options": {} },
      "id": "b9010000-0000-4000-8000-000000000010",
      "name": "Knowledge Search Successful?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [80, -140]
    },
    {
      "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({ ok: false, status: 'knowledge-service-unavailable', retryable: true, error: 'Verified knowledge search failed' }) }}", "options": { "responseCode": 503 } },
      "id": "b9010000-0000-4000-8000-000000000011",
      "name": "Return Knowledge Failure",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [320, 20]
    },
    {
      "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "conditions": [{ "id": "b9010000-0000-4000-8000-000000000012", "leftValue": "={{ $json.answerSupported === true && Number($json.confidence) >= 0.85 && Boolean($json.citationId) }}", "rightValue": true, "operator": { "type": "boolean", "operation": "true", "singleValue": true } }], "combinator": "and" }, "options": {} },
      "id": "b9010000-0000-4000-8000-000000000013",
      "name": "Grounded Answer Available?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [320, -180]
    },
    {
      "parameters": { "method": "POST", "url": "http://127.0.0.1:8787/grounded-support/drafts", "sendBody": true, "contentType": "raw", "rawContentType": "application/json", "body": "={{ JSON.stringify({ providerEventId: $('Validate and Guard Ticket').item.json.providerEventId, ticketId: $('Validate and Guard Ticket').item.json.ticketId, question: $('Validate and Guard Ticket').item.json.question, confidence: $json.confidence, citationId: $json.citationId, citationTitle: $json.citationTitle, idempotencyKey: `${$('Validate and Guard Ticket').item.json.idempotencyKey}:draft`, simulateTicketingFailure: $('Validate and Guard Ticket').item.json.simulateTicketingFailure }) }}", "options": { "timeout": 10000 } },
      "id": "b9010000-0000-4000-8000-000000000014",
      "name": "Create Reviewable Draft",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [580, -280],
      "onError": "continueRegularOutput"
    },
    {
      "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "conditions": [{ "id": "b9010000-0000-4000-8000-000000000015", "leftValue": "={{ Boolean($json.draftId) }}", "rightValue": true, "operator": { "type": "boolean", "operation": "true", "singleValue": true } }], "combinator": "and" }, "options": {} },
      "id": "b9010000-0000-4000-8000-000000000016",
      "name": "Draft Created?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [820, -280]
    },
    {
      "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({ ok: true, status: 'grounded-draft-ready', draft: $json, autoSent: false }) }}", "options": { "responseCode": 200 } },
      "id": "b9010000-0000-4000-8000-000000000017",
      "name": "Return Grounded Draft",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [1060, -360]
    },
    {
      "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({ ok: false, status: 'ticketing-service-unavailable', retryable: true, error: 'Draft persistence failed' }) }}", "options": { "responseCode": 503 } },
      "id": "b9010000-0000-4000-8000-000000000018",
      "name": "Return Ticketing Failure",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [1060, -200]
    },
    {
      "parameters": { "method": "POST", "url": "http://127.0.0.1:8787/grounded-support/handoffs", "sendBody": true, "contentType": "raw", "rawContentType": "application/json", "body": "={{ JSON.stringify({ providerEventId: $('Validate and Guard Ticket').item.json.providerEventId, ticketId: $('Validate and Guard Ticket').item.json.ticketId, reason: $('Validate and Guard Ticket').item.json.handoffReason || 'low-confidence-or-no-verified-source', idempotencyKey: `${$('Validate and Guard Ticket').item.json.idempotencyKey}:handoff` }) }}", "options": { "timeout": 10000 } },
      "id": "b9010000-0000-4000-8000-000000000019",
      "name": "Create Support Handoff",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [580, 60]
    },
    {
      "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({ ok: true, status: 'human-handoff', handoff: $json, customerMessagesSent: 0 }) }}", "options": { "responseCode": 202 } },
      "id": "b9010000-0000-4000-8000-000000000020",
      "name": "Return Support Handoff",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [820, 60]
    }
  ],
  "connections": {
    "Support Ticket Event": { "main": [[{ "node": "Validate and Guard Ticket", "type": "main", "index": 0 }]] },
    "Validate and Guard Ticket": { "main": [[{ "node": "Valid Ticket?", "type": "main", "index": 0 }]] },
    "Valid Ticket?": { "main": [[{ "node": "Immediate Handoff Required?", "type": "main", "index": 0 }], [{ "node": "Return Invalid Ticket", "type": "main", "index": 0 }]] },
    "Immediate Handoff Required?": { "main": [[{ "node": "Create Support Handoff", "type": "main", "index": 0 }], [{ "node": "Search Verified Knowledge", "type": "main", "index": 0 }]] },
    "Search Verified Knowledge": { "main": [[{ "node": "Knowledge Search Successful?", "type": "main", "index": 0 }]] },
    "Knowledge Search Successful?": { "main": [[{ "node": "Grounded Answer Available?", "type": "main", "index": 0 }], [{ "node": "Return Knowledge Failure", "type": "main", "index": 0 }]] },
    "Grounded Answer Available?": { "main": [[{ "node": "Create Reviewable Draft", "type": "main", "index": 0 }], [{ "node": "Create Support Handoff", "type": "main", "index": 0 }]] },
    "Create Reviewable Draft": { "main": [[{ "node": "Draft Created?", "type": "main", "index": 0 }]] },
    "Draft Created?": { "main": [[{ "node": "Return Grounded Draft", "type": "main", "index": 0 }], [{ "node": "Return Ticketing Failure", "type": "main", "index": 0 }]] },
    "Create Support Handoff": { "main": [[{ "node": "Return Support Handoff", "type": "main", "index": 0 }]] }
  },
  "settings": { "executionOrder": "v1" },
  "pinData": {},
  "versionId": "b9010000-0000-4000-8000-000000000099",
  "meta": { "templateCredsSetupCompleted": true },
  "tags": []
}
