{
  "name": "Mako | Fictional lead handoff demo | No CRM sync",
  "nodes": [
    {
      "parameters": {},
      "id": "demo-manual-start",
      "name": "Start fictional demo manually",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        300
      ],
      "notes": "Isolated demo. No credentials, API calls, CRM writes or messages.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "// Fictional fixture only. .example domains do not receive email.\nconst rows = [\n  { source_id: 'demo-01', full_name: ' Alex Morgan ', email: ' Alex@AcornCloud.example ', company: ' Acorn Cloud ', website: 'https://www.acorncloud.example/contact', source: 'fictional website form' },\n  { source_id: 'demo-02', full_name: 'Alex Morgan', email: 'alex@acorncloud.example', company: 'Acorn   Cloud', website: 'acorncloud.example', source: 'fictional repeat form' },\n  { source_id: 'demo-03', full_name: 'Maria Costa', email: 'maria@cedarlabs.example', company: 'Cedar Labs', website: 'https://cedarlabs.example', source: 'fictional website form' },\n  { source_id: 'demo-04', full_name: 'Lee Park', email: 'lee@willowdata.example', company: '', website: 'willowdata.example', source: 'fictional website form' },\n  { source_id: 'demo-05', full_name: 'Sam Taylor', email: '', company: 'Pine Systems', website: 'pinesystems.example', source: 'fictional website form' },\n  { source_id: 'demo-06', full_name: 'Jordan Reed', email: 'jordan@birchapps.example', company: 'Birch Apps', website: 'birchapps.example', source: 'fictional website form' },\n  { source_id: 'demo-07', full_name: 'Jordan Reed', email: ' JORDAN@BIRCHAPPS.EXAMPLE ', company: 'Maple Studio', website: 'maplestudio.example', source: 'fictional conflicting import' },\n  { source_id: 'demo-08', full_name: 'Robin Ellis', email: 'robin-at-elm.example', company: 'Elm Tools', website: 'elm.example', source: 'fictional website form' }\n];\nreturn rows.map(row => ({ json: { demo_data: true, demo_label: 'FICTIONAL DEMO DATA', ...row } }));"
      },
      "id": "demo-fictional-fixture",
      "name": "Load 8 fictional form records",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        260,
        300
      ],
      "notes": "Fixed fictional names and reserved .example domains. No external source is connected.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "// Local, deterministic rules. These are format checks, not identity or mailbox verification.\nconst clean = value => String(value ?? '').trim().replace(/\\s+/g, ' ');\nconst domainOf = value => {\n  const text = clean(value).toLowerCase();\n  if (!text) return '';\n  // Deliberately bounded fixture parser: plain domain or HTTP(S) website only.\n  const match = text.match(/^(?:https?:\\/\\/)?(?:www\\.)?([a-z0-9](?:[a-z0-9.-]*[a-z0-9])?)(?::\\d+)?(?:[/?#].*)?$/);\n  if (!match) return '';\n  const labels = match[1].split('.');\n  return labels.length >= 2 && labels.every(label => /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(label)) ? match[1] : '';\n};\nconst input = $input.all();\nconst rows = input.map((item, index) => {\n  const raw = item.json;\n  if (raw.demo_data !== true || !String(raw.source_id || '').startsWith('demo-')) {\n    throw new Error('This isolated demo accepts only explicitly labelled fictional fixture records.');\n  }\n  const normalized = {\n    full_name: clean(raw.full_name),\n    email: clean(raw.email).toLowerCase(),\n    company: clean(raw.company),\n    company_domain: domainOf(raw.website),\n    source: clean(raw.source)\n  };\n  const reasons = [];\n  if (!normalized.full_name) reasons.push('missing_full_name');\n  if (!normalized.email) reasons.push('missing_email');\n  else if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(normalized.email)) reasons.push('invalid_email_format');\n  if (!normalized.company) reasons.push('missing_company');\n  if (!normalized.company_domain) reasons.push('missing_or_invalid_company_website');\n  if (!normalized.source) reasons.push('missing_source');\n  return { index, source_id: raw.source_id, raw, normalized, reasons, status: 'held', duplicate_of: null, conflicting_source_ids: [] };\n});\n// Exact normalized email groups. A shared name alone never joins two people.\nconst groups = new Map();\nfor (const row of rows) {\n  if (!row.normalized.email || row.reasons.includes('invalid_email_format')) continue;\n  const key = row.normalized.email;\n  if (!groups.has(key)) groups.set(key, []);\n  groups.get(key).push(row);\n}\nfor (const group of groups.values()) {\n  const companies = new Set(group.map(row => row.normalized.company.toLowerCase()).filter(Boolean));\n  const domains = new Set(group.map(row => row.normalized.company_domain).filter(Boolean));\n  const names = new Set(group.map(row => row.normalized.full_name.toLowerCase()).filter(Boolean));\n  const companyConflict = companies.size > 1 || domains.size > 1;\n  const identityConflict = names.size > 1;\n  if (companyConflict || identityConflict) {\n    const ids = group.map(row => row.source_id).sort();\n    for (const row of group) {\n      if (companyConflict) row.reasons.push('conflicting_company_for_same_email');\n      if (identityConflict) row.reasons.push('conflicting_name_for_same_email');\n      row.conflicting_source_ids = ids;\n    }\n    continue;\n  }\n  // Pick the lexically first valid fixture ID, independent of the incoming item order.\n  const eligible = group.filter(row => row.reasons.length === 0).sort((a, b) => a.source_id < b.source_id ? -1 : a.source_id > b.source_id ? 1 : 0);\n  if (!eligible.length) continue;\n  eligible[0].status = 'prepared';\n  for (const row of eligible.slice(1)) {\n    row.reasons.push('duplicate_email_in_this_batch');\n    row.duplicate_of = eligible[0].source_id;\n  }\n}\nreturn rows.map(row => ({\n  json: {\n    demo_data: true,\n    demo_label: 'FICTIONAL DEMO DATA',\n    source_id: row.source_id,\n    status: row.status,\n    hold_reasons: row.reasons,\n    duplicate_of: row.duplicate_of,\n    conflicting_source_ids: row.conflicting_source_ids,\n    normalized: row.normalized,\n    original: row.raw,\n    verification: { format_checked_only: true, mailbox_verified: false, identity_verified: false, crm_checked: false }\n  },\n  pairedItem: { item: row.index }\n}));"
      },
      "id": "demo-normalize-check",
      "name": "Normalize, validate and deduplicate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        540,
        300
      ],
      "notes": "Format checks and exact-email matching within this batch. Conflicting companies stay held.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "demo-prepared-condition",
              "leftValue": "={{ $json.status }}",
              "rightValue": "prepared",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "demo-route-result",
      "name": "Prepared for CRM mapping?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        850,
        300
      ],
      "notes": "True: 2 prepared examples. False: 6 held examples. Neither branch writes externally.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "// Prepared payload only. This node has no CRM connector and performs no sync.\nreturn $input.all().map((item, index) => ({\n  json: {\n    demo_data: true,\n    demo_label: 'FICTIONAL DEMO DATA',\n    source_id: item.json.source_id,\n    status: 'prepared_output_only',\n    connector_state: 'NOT CONNECTED — no CRM read or write performed',\n    target: 'generic CRM contact mapping example',\n    proposed_operation: 'review_then_upsert_by_email',\n    match_key: item.json.normalized.email,\n    payload: {\n      full_name: item.json.normalized.full_name,\n      email: item.json.normalized.email,\n      company_name: item.json.normalized.company,\n      company_domain: item.json.normalized.company_domain,\n      lead_source: item.json.normalized.source\n    },\n    checks: item.json.verification,\n    original: item.json.original\n  },\n  pairedItem: { item: index }\n}));"
      },
      "id": "demo-prepared-output",
      "name": "Prepared CRM payload (no sync)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1140,
        140
      ],
      "notes": "OUTPUT PREVIEW ONLY. Generic mapping; actual CRM integration is not installed.",
      "notesInFlow": true
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "language": "javaScript",
        "jsCode": "// Every held item remains visible with its original values and review reason.\nreturn $input.all().map((item, index) => ({\n  json: {\n    demo_data: true,\n    demo_label: 'FICTIONAL DEMO DATA',\n    source_id: item.json.source_id,\n    status: 'held_for_review',\n    hold_reasons: item.json.hold_reasons,\n    duplicate_of: item.json.duplicate_of,\n    conflicting_source_ids: item.json.conflicting_source_ids,\n    normalized: item.json.normalized,\n    original: item.json.original,\n    next_action: item.json.duplicate_of\n      ? 'Inspect the canonical fixture record; do not create another contact.'\n      : 'Resolve the recorded data issue before considering any CRM write.'\n  },\n  pairedItem: { item: index }\n}));"
      },
      "id": "demo-held-output",
      "name": "Held records with reasons",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1140,
        470
      ],
      "notes": "Review output preserves duplicates, missing fields and both conflicting company records.",
      "notesInFlow": true
    }
  ],
  "connections": {
    "Start fictional demo manually": {
      "main": [
        [
          {
            "node": "Load 8 fictional form records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load 8 fictional form records": {
      "main": [
        [
          {
            "node": "Normalize, validate and deduplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize, validate and deduplicate": {
      "main": [
        [
          {
            "node": "Prepared for CRM mapping?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepared for CRM mapping?": {
      "main": [
        [
          {
            "node": "Prepared CRM payload (no sync)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Held records with reasons",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "tags": []
}
