{
  "id": "leadHandoffRescue01",
  "name": "One Workflow Fixed - Lead Handoff Rescue Demo",
  "nodes": [
    {
      "parameters": {},
      "id": "d36cab49-b78f-457a-a70d-7a44847cadb4",
      "name": "Run Rescue Tests",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -560,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "return [\n  {\n    json: {\n      case_id: 'T-01',\n      event_id: 'evt-current-001',\n      email: 'current@example.test',\n      phone_number: '+1 (312) 555-0101',\n      expected: 'create'\n    }\n  },\n  {\n    json: {\n      case_id: 'T-02',\n      event_id: 'evt-legacy-002',\n      email: 'legacy@example.test',\n      phone: '312-555-0102',\n      expected: 'create_from_legacy'\n    }\n  },\n  {\n    json: {\n      case_id: 'T-03',\n      event_id: 'evt-invalid-003',\n      email: 'missing-phone@example.test',\n      expected: 'exception'\n    }\n  },\n  {\n    json: {\n      case_id: 'T-04-A',\n      event_id: 'evt-repeat-004',\n      email: 'repeat@example.test',\n      phone_number: '+1 312 555 0104',\n      name: 'Original Name',\n      expected: 'create'\n    }\n  },\n  {\n    json: {\n      case_id: 'T-04-B',\n      event_id: 'evt-repeat-004',\n      email: 'repeat@example.test',\n      phone_number: '+1 312 555 0104',\n      name: 'Updated Name',\n      expected: 'update_existing'\n    }\n  }\n];"
      },
      "id": "297f3c66-d166-439f-8976-d62116b2e17f",
      "name": "Generate Test Cases",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -320,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const sourceItems = $input.all();\nconst seenEvents = new Map();\n\nfunction normalizeUsPhone(value) {\n  if (typeof value !== 'string') return null;\n  const digits = value.replace(/\\D/g, '');\n  if (digits.length === 10) return `+1${digits}`;\n  if (digits.length === 11 && digits.startsWith('1')) return `+${digits}`;\n  return null;\n}\n\nreturn sourceItems.map((item) => {\n  const input = item.json;\n  const rawPhone = input.phone_number ?? input.phone ?? null;\n  const sourceField = input.phone_number ? 'phone_number' : input.phone ? 'phone' : null;\n  const phone = normalizeUsPhone(rawPhone);\n\n  if (!phone) {\n    return {\n      json: {\n        ...input,\n        source_field: sourceField,\n        normalized_phone: null,\n        route: 'exception_queue',\n        action: 'blocked',\n        alert_owner: true,\n        reason: 'Missing or invalid phone value'\n      }\n    };\n  }\n\n  const isDuplicateEvent = seenEvents.has(input.event_id);\n  const action = isDuplicateEvent ? 'update_existing' : 'create';\n  seenEvents.set(input.event_id, {\n    email: input.email,\n    phone,\n    latest_name: input.name ?? null\n  });\n\n  return {\n    json: {\n      ...input,\n      source_field: sourceField,\n      normalized_phone: phone,\n      route: 'crm_ready',\n      action,\n      alert_owner: false,\n      duplicate_event: isDuplicateEvent\n    }\n  };\n});"
      },
      "id": "4390d7fa-670f-42aa-939a-7030933ce5c9",
      "name": "Normalize and De-duplicate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -40,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const rows = $input.all().map((item) => item.json);\nconst byCase = Object.fromEntries(rows.map((row) => [row.case_id, row]));\n\nconst tests = [\n  {\n    id: 'T-01',\n    description: 'Current phone_number field creates a CRM-ready record',\n    passed:\n      byCase['T-01']?.route === 'crm_ready' &&\n      byCase['T-01']?.action === 'create' &&\n      byCase['T-01']?.normalized_phone === '+13125550101'\n  },\n  {\n    id: 'T-02',\n    description: 'Legacy phone field is accepted and normalized',\n    passed:\n      byCase['T-02']?.route === 'crm_ready' &&\n      byCase['T-02']?.source_field === 'phone' &&\n      byCase['T-02']?.normalized_phone === '+13125550102'\n  },\n  {\n    id: 'T-03',\n    description: 'Missing phone is blocked and routed to the exception queue',\n    passed:\n      byCase['T-03']?.route === 'exception_queue' &&\n      byCase['T-03']?.action === 'blocked' &&\n      byCase['T-03']?.alert_owner === true\n  },\n  {\n    id: 'T-04',\n    description: 'Repeated event updates the existing record instead of creating a duplicate',\n    passed:\n      byCase['T-04-A']?.action === 'create' &&\n      byCase['T-04-B']?.action === 'update_existing' &&\n      byCase['T-04-B']?.duplicate_event === true\n  }\n];\n\nconst allPassed = tests.every((test) => test.passed);\nconst uniqueReadyEvents = new Set(\n  rows.filter((row) => row.route === 'crm_ready').map((row) => row.event_id)\n);\n\nreturn [\n  {\n    json: {\n      workflow: 'One Workflow Fixed - Lead Handoff Rescue Demo',\n      all_passed: allPassed,\n      tests: tests.map((test) => ({\n        ...test,\n        result: test.passed ? 'PASS' : 'FAIL'\n      })),\n      evidence: {\n        input_events: rows.length,\n        crm_ready_events: uniqueReadyEvents.size,\n        exception_events: rows.filter((row) => row.route === 'exception_queue').length,\n        duplicate_updates: rows.filter((row) => row.action === 'update_existing').length\n      },\n      processed_records: rows,\n      note: 'Credential-free demonstration. Replace simulated CRM routing with the client-approved node or API only after scope and access approval.'\n    }\n  }\n];"
      },
      "id": "6ba2a0ac-9555-4d79-a1ba-5ac5bc7b14d9",
      "name": "Build Verification Report",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        0
      ]
    },
    {
      "parameters": {
        "content": "## Lead handoff rescue demo\n\nA credential-free workflow that proves four behaviors:\n\n1. current field mapping\n2. legacy field compatibility\n3. invalid-data exception routing\n4. duplicate-event update behavior\n\nOpen **Build Verification Report** after execution. `all_passed` must be `true`.",
        "height": 300,
        "width": 360,
        "color": 5
      },
      "id": "694da933-ab3a-4473-ac6b-af17ad9d81db",
      "name": "How to verify",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -560,
        -400
      ]
    },
    {
      "parameters": {
        "content": "## Safety boundary\n\nThis demo performs no network calls, sends no messages, changes no CRM records, and contains no credentials or client data.\n\nIt models the decision logic that would sit before a client-approved CRM node.",
        "height": 260,
        "width": 360,
        "color": 4
      },
      "id": "00a9ec6d-3c90-454c-9e11-5c6e28de4315",
      "name": "No external actions",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -40,
        -400
      ]
    }
  ],
  "connections": {
    "Run Rescue Tests": {
      "main": [
        [
          {
            "node": "Generate Test Cases",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Test Cases": {
      "main": [
        [
          {
            "node": "Normalize and De-duplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize and De-duplicate": {
      "main": [
        [
          {
            "node": "Build Verification Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "834ad6b7-8c8d-45f2-a22a-e7d7b5ffc7bf",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "tags": []
}
