AutomationFlowsMarketing & Ads › Lead Intake Normalizer & Deduplicator (http Request)

Lead Intake Normalizer & Deduplicator (http Request)

Lead Intake Normalizer & Deduplicator. Uses httpRequest. Webhook trigger; 22 nodes.

Webhook trigger★★★★☆ complexity22 nodesHTTP Request
Marketing & Ads Trigger: Webhook Nodes: 22 Complexity: ★★★★☆ Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Lead Intake Normalizer & Deduplicator",
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveManualExecutions": true
  },
  "nodes": [
    {
      "name": "Lead Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -1120,
        300
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "lead-intake",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "name": "Normalize & Validate Lead",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -900,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": {
          "$code": "code/01-normalize-lead.js"
        }
      }
    },
    {
      "name": "Lead Usable?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -680,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "lead-usable",
              "leftValue": "={{ $json.isUsable }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "name": "Prepare CRM Lookup",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -460,
        180
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": {
          "$code": "code/02-prepare-crm-lookup.js"
        }
      }
    },
    {
      "name": "Use Live CRM?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -240,
        180
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "live-crm",
              "leftValue": "={{ $json.mockMode !== true && $json.canSearch === true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "name": "CRM: Find Contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -20,
        60
      ],
      "alwaysOutputData": false,
      "parameters": {
        "method": "GET",
        "url": "https://services.leadconnectorhq.com/contacts/",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "specifyQuery": "keypair",
        "queryParameters": {
          "parameters": [
            {
              "name": "locationId",
              "value": "={{ $('Prepare CRM Lookup').item.json.locationId }}"
            },
            {
              "name": "query",
              "value": "={{ $('Prepare CRM Lookup').item.json.query.phone || $('Prepare CRM Lookup').item.json.query.email }}"
            },
            {
              "name": "limit",
              "value": "20"
            }
          ]
        },
        "sendHeaders": true,
        "specifyHeaders": "keypair",
        "headerParameters": {
          "parameters": [
            {
              "name": "Version",
              "value": "2021-07-28"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true
            }
          }
        }
      }
    },
    {
      "name": "Classify Search Error",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        200,
        -120
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": {
          "$code": "code/04-classify-api-error.js"
        }
      }
    },
    {
      "name": "Retry Search?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        420,
        -120
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "should-retry",
              "leftValue": "={{ $json.shouldRetry }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "name": "Backoff Wait",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        420,
        -320
      ],
      "parameters": {
        "amount": "={{ $json.retry.waitSeconds }}",
        "unit": "seconds"
      }
    },
    {
      "name": "Decide Create or Update",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        180
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": {
          "$code": "code/03-decide-create-or-update.js"
        }
      }
    },
    {
      "name": "Needs a CRM Write?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        660,
        180
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "needs-write",
              "leftValue": "={{ $json.action !== 'skip' && $json.mockMode !== true }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "name": "Create or Update?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        880,
        80
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "is-create",
              "leftValue": "={{ $json.action === 'create' }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "name": "CRM: Create Contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        -60
      ],
      "parameters": {
        "method": "POST",
        "url": "https://services.leadconnectorhq.com/contacts/",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "specifyHeaders": "keypair",
        "headerParameters": {
          "parameters": [
            {
              "name": "Version",
              "value": "2021-07-28"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify(Object.assign({}, $json.payload, { locationId: $json.locationId })) }}",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true
            }
          }
        }
      }
    },
    {
      "name": "CRM: Update Contact",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1120,
        240
      ],
      "parameters": {
        "method": "PUT",
        "url": "=https://services.leadconnectorhq.com/contacts/{{ $json.contactId }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "specifyHeaders": "keypair",
        "headerParameters": {
          "parameters": [
            {
              "name": "Version",
              "value": "2021-07-28"
            },
            {
              "name": "Accept",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.payload) }}",
        "options": {
          "timeout": 15000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true
            }
          }
        }
      }
    },
    {
      "name": "Classify Write Error",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1360,
        -80
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": {
          "$code": "code/04-classify-api-error.js"
        }
      }
    },
    {
      "name": "Resolve Without Writing",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        880,
        380
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": {
          "$code": "code/05-resolve-without-writing.js"
        }
      }
    },
    {
      "name": "Build Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1600,
        260
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": {
          "$code": "code/06-build-response.js"
        }
      }
    },
    {
      "name": "Respond to Source",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1820,
        260
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}",
        "options": {
          "responseCode": "={{ $json.statusCode }}"
        }
      }
    },
    {
      "name": "README",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1140,
        -80
      ],
      "parameters": {
        "width": 460,
        "height": 300,
        "color": 4,
        "content": "## Lead Intake Normalizer\n\nPOST any lead payload to this webhook \u2014 website form, Facebook Lead Ads, Typeform, or a partner's own envelope \u2014 and it comes out as one clean contact or gets quarantined with a reason code.\n\n**Runs with no credentials.** `MOCK_MODE = true` in **Prepare CRM Lookup** makes the workflow answer its own CRM search from fixtures. Execute the workflow, POST the sample payload, and watch it work.\n\nTo go live: set `MOCK_MODE = false`, set `LOCATION_ID`, and attach a Header Auth credential to the three CRM nodes."
      }
    },
    {
      "name": "Error handling note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        180,
        -420
      ],
      "parameters": {
        "width": 460,
        "height": 240,
        "color": 3,
        "content": "## Why the retry loop is hand-built\n\nn8n's node-level **Retry On Fail** is ignored when **On Error** is set to a Continue option, so you cannot have both built-in retries and a graceful error branch.\n\nThese HTTP nodes use **Full Response + Never Error** instead of the error output, so a 429 arrives as a normal item with its `statusCode` and `headers` intact \u2014 which is the only way to read `Retry-After` at all. An IF routes non-2xx to the classifier.\n\nOnly the **search** retries. A failed create/update is never blindly repeated \u2014 a retried POST is how you get duplicate contacts."
      }
    },
    {
      "name": "Search Succeeded?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        200,
        60
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "search-succeeded",
              "leftValue": "={{ $json.statusCode >= 200 && $json.statusCode < 300 }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    },
    {
      "name": "Write Succeeded?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1120,
        80
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "write-succeeded",
              "leftValue": "={{ $json.statusCode >= 200 && $json.statusCode < 300 }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      }
    }
  ],
  "connections": {
    "Lead Webhook": {
      "main": [
        [
          {
            "node": "Normalize & Validate Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize & Validate Lead": {
      "main": [
        [
          {
            "node": "Lead Usable?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lead Usable?": {
      "main": [
        [
          {
            "node": "Prepare CRM Lookup",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare CRM Lookup": {
      "main": [
        [
          {
            "node": "Use Live CRM?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Use Live CRM?": {
      "main": [
        [
          {
            "node": "CRM: Find Contact",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Decide Create or Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CRM: Find Contact": {
      "main": [
        [
          {
            "node": "Search Succeeded?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Search Error": {
      "main": [
        [
          {
            "node": "Retry Search?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retry Search?": {
      "main": [
        [
          {
            "node": "Backoff Wait",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Build Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Backoff Wait": {
      "main": [
        [
          {
            "node": "CRM: Find Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decide Create or Update": {
      "main": [
        [
          {
            "node": "Needs a CRM Write?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Needs a CRM Write?": {
      "main": [
        [
          {
            "node": "Create or Update?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Resolve Without Writing",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create or Update?": {
      "main": [
        [
          {
            "node": "CRM: Create Contact",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "CRM: Update Contact",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CRM: Create Contact": {
      "main": [
        [
          {
            "node": "Write Succeeded?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CRM: Update Contact": {
      "main": [
        [
          {
            "node": "Write Succeeded?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Write Error": {
      "main": [
        [
          {
            "node": "Build Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Resolve Without Writing": {
      "main": [
        [
          {
            "node": "Build Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Response": {
      "main": [
        [
          {
            "node": "Respond to Source",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Succeeded?": {
      "main": [
        [
          {
            "node": "Decide Create or Update",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Classify Search Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Succeeded?": {
      "main": [
        [
          {
            "node": "Build Response",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Classify Write Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Lead Intake Normalizer & Deduplicator. Uses httpRequest. Webhook trigger; 22 nodes.

Source: https://github.com/ihthicodes/n8n-agency-demos/blob/main/demos/01-lead-intake-normalizer/workflow.src.json — original creator credit. Request a take-down →

More Marketing & Ads workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Marketing & Ads

This workflow automates bulk email campaigns with built-in validation, deliverability protection, and smart send-time optimization.

HTTP Request, Postgres, Gmail
Marketing & Ads

Universal Lead Processing Workflow. Uses googleSheets, gmail, httpRequest, gmailTrigger. Webhook trigger; 34 nodes.

Google Sheets, Gmail, HTTP Request +1
Marketing & Ads

This workflow turns Feishu bot messages into a human-in-the-loop short-video production pipeline, using Tavily for web search, DeepSeek for topic and script generation, Jimeng AI for text-to-video, an

HTTP Request
Marketing & Ads

This workflow is designed to manage the assignment and validation of unique QR code coupons within a lead generation system with SuiteCRM.

HTTP Request, Form Trigger, Google Sheets +1
Marketing & Ads

This workflow acts as an instant SDR that replies to new inbound leads across multiple channels in real time. It first captures and normalizes all incoming lead data into a unified structure. The work

Google Sheets, HTTP Request, Gmail +1