{
  "name": "Template | Normalize and Deduplicate Leads",
  "nodes": [
    {
      "id": "16fe30ea-eca3-4c75-a3f5-d42082251fe7",
      "name": "Run Manually",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ],
      "parameters": {}
    },
    {
      "id": "5d173938-6f06-4065-a2e1-36387d8ffdf8",
      "name": "Example Lead Input",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        260,
        0
      ],
      "parameters": {
        "mode": "manual",
        "assignments": {
          "assignments": [
            {
              "id": "leads",
              "name": "leads",
              "value": [
                {
                  "name": " Ada Example ",
                  "email": " ADA@example.com ",
                  "company": " Example Co "
                },
                {
                  "name": "Ada Duplicate",
                  "email": "ada@example.com",
                  "company": "Example Co"
                },
                {
                  "name": "Ben Example",
                  "email": "ben@example.org",
                  "company": "Demo Co"
                },
                {
                  "name": "Invalid Example",
                  "email": "not-an-email",
                  "company": "Demo Co"
                }
              ],
              "type": "array"
            }
          ]
        }
      }
    },
    {
      "id": "f18872ba-400c-4773-814c-707e4efcaac6",
      "name": "Normalize and Deduplicate",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        520,
        0
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const input = $input.first().json.leads;\nif (!Array.isArray(input)) throw new Error('Expected a leads array');\nconst seen = new Set();\nconst leads = [];\nconst rejected = [];\nlet duplicatesRemoved = 0;\nfor (let index = 0; index < input.length; index++) {\n  const raw = input[index];\n  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n    rejected.push({index,reason:'Record must be an object'});\n    continue;\n  }\n  const email = typeof raw.email === 'string' ? raw.email.trim().toLowerCase() : '';\n  const name = typeof raw.name === 'string' ? raw.name.trim().replace(/\\s+/g,' ') : '';\n  const company = typeof raw.company === 'string' ? raw.company.trim().replace(/\\s+/g,' ') : '';\n  if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email)) {\n    rejected.push({index,reason:'Missing or invalid email format'});\n    continue;\n  }\n  if (seen.has(email)) { duplicatesRemoved++; continue; }\n  seen.add(email);\n  leads.push({name,email,company});\n}\nreturn [{json:{leads,summary:{received:input.length,accepted:leads.length,duplicatesRemoved,rejected:rejected.length},rejected}}];\n"
      }
    },
    {
      "id": "21a4a6fe-7ed4-4659-881c-306d423f1c6b",
      "name": "Sticky Note c9abdd73",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -360
      ],
      "parameters": {
        "content": "## Lead cleanup template\nRun the fictional example, then replace Example Lead Input with your own form, CRM, or webhook source that supplies a leads array. Normalizes names, emails and companies; keeps the first record per email; reports duplicates and invalid records. Does not contact leads or write to any external service.",
        "width": 650,
        "height": 260
      }
    }
  ],
  "connections": {
    "Run Manually": {
      "main": [
        [
          {
            "node": "Example Lead Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Example Lead Input": {
      "main": [
        [
          {
            "node": "Normalize and Deduplicate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "active": false,
  "pinData": {}
}
