{
  "name": "Waterfall Enrichment — Apollo + Clearbit + PDL → HubSpot",
  "nodes": [
    {
      "parameters": {
        "event": "contactCreated",
        "additionalFields": {}
      },
      "id": "hubspot-trigger",
      "name": "HubSpot Trigger",
      "type": "n8n-nodes-base.hubspotTrigger",
      "typeVersion": 1,
      "position": [0, 0],
      "credentials": {
        "hubspotApi": {
          "id": "credential-id",
          "name": "HubSpot API"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.apollo.io/api/v1/people/match",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $credentials.apolloApi.apiKey }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"email\": \"{{ $json.properties.email }}\"\n}",
        "options": {}
      },
      "id": "apollo-enrich",
      "name": "Apollo Enrich",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [220, 0]
    },
    {
      "parameters": {
        "jsCode": "const person = $input.first().json.person;\nconst contactId = $('HubSpot Trigger').first().json.id;\nconst email = $('HubSpot Trigger').first().json.properties.email;\n\nconst fields = {\n  jobtitle: person?.title || null,\n  company: person?.organization?.name || null,\n  phone: person?.phone_numbers?.[0]?.sanitized_number || null,\n  linkedin_url: person?.linkedin_url || null,\n  industry: person?.organization?.industry || null,\n  seniority: person?.seniority || null,\n};\n\nconst missingFields = Object.entries(fields)\n  .filter(([_, v]) => !v)\n  .map(([k]) => k);\n\nreturn [{\n  json: {\n    contactId,\n    email,\n    fields,\n    missingFields,\n    source: \"apollo\",\n    needsFallback: missingFields.length > 0,\n  }\n}];"
      },
      "id": "check-missing",
      "name": "Check Missing Fields",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [440, 0]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "leftValue": "={{ $json.needsFallback }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "if-needs-clearbit",
      "name": "Needs Clearbit?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [660, 0]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://person.clearbit.com/v2/people/find?email={{ $json.email }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $credentials.clearbitApi.apiKey }}"
            }
          ]
        },
        "options": {}
      },
      "id": "clearbit-enrich",
      "name": "Clearbit Enrich",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [880, 0]
    },
    {
      "parameters": {
        "jsCode": "const existing = $('Check Missing Fields').first().json;\nconst clearbit = $input.first().json;\nconst fields = { ...existing.fields };\n\n// Only fill fields that are still null\nif (!fields.jobtitle && clearbit.title) fields.jobtitle = clearbit.title;\nif (!fields.company && clearbit.employment?.name) fields.company = clearbit.employment.name;\nif (!fields.seniority && clearbit.employment?.seniority) fields.seniority = clearbit.employment.seniority;\nif (!fields.linkedin_url && clearbit.linkedin?.handle) {\n  fields.linkedin_url = `https://linkedin.com/in/${clearbit.linkedin.handle}`;\n}\n\nconst stillMissing = Object.entries(fields)\n  .filter(([_, v]) => !v)\n  .map(([k]) => k);\n\nreturn [{\n  json: {\n    ...existing,\n    fields,\n    missingFields: stillMissing,\n    source: stillMissing.length < existing.missingFields.length ? \"apollo+clearbit\" : existing.source,\n    needsFallback: stillMissing.length > 0,\n  }\n}];"
      },
      "id": "merge-clearbit",
      "name": "Merge Clearbit Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1100, 0]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "leftValue": "={{ $json.needsFallback }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        }
      },
      "id": "if-needs-pdl",
      "name": "Needs PDL?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [1320, 0]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.peopledatalabs.com/v5/person/enrich",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "x-api-key",
              "value": "={{ $credentials.pdlApi.apiKey }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"email\": \"{{ $json.email }}\"\n}",
        "options": {}
      },
      "id": "pdl-enrich",
      "name": "PDL Enrichment",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [1540, 0]
    },
    {
      "parameters": {
        "jsCode": "const existing = $('Merge Clearbit Data').first().json;\nconst pdl = $input.first().json.data || $input.first().json;\nconst fields = { ...existing.fields };\n\nif (!fields.jobtitle && pdl.job_title) fields.jobtitle = pdl.job_title;\nif (!fields.company && pdl.job_company_name) fields.company = pdl.job_company_name;\nif (!fields.phone && pdl.phone_numbers?.[0]) fields.phone = pdl.phone_numbers[0];\nif (!fields.linkedin_url && pdl.linkedin_url) fields.linkedin_url = pdl.linkedin_url;\nif (!fields.industry && pdl.industry) fields.industry = pdl.industry;\n\nconst sources = [existing.source, \"pdl\"].filter(Boolean);\n\nreturn [{\n  json: {\n    ...existing,\n    fields,\n    source: sources.join(\"+\"),\n  }\n}];"
      },
      "id": "merge-pdl",
      "name": "Merge PDL Data",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [1760, 0]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "=https://api.hubapi.com/crm/v3/objects/contacts/{{ $json.contactId }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "hubspotApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"properties\": {\n    \"jobtitle\": \"{{ $json.fields.jobtitle }}\",\n    \"company\": \"{{ $json.fields.company }}\",\n    \"phone\": \"{{ $json.fields.phone }}\",\n    \"linkedin_url\": \"{{ $json.fields.linkedin_url }}\",\n    \"industry\": \"{{ $json.fields.industry }}\",\n    \"enrichment_source\": \"{{ $json.source }}\"\n  }\n}",
        "options": {}
      },
      "id": "update-hubspot",
      "name": "Update HubSpot",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [2200, 0],
      "credentials": {
        "hubspotApi": {
          "id": "credential-id",
          "name": "HubSpot API"
        }
      }
    }
  ],
  "connections": {
    "HubSpot Trigger": {
      "main": [[{ "node": "Apollo Enrich", "type": "main", "index": 0 }]]
    },
    "Apollo Enrich": {
      "main": [[{ "node": "Check Missing Fields", "type": "main", "index": 0 }]]
    },
    "Check Missing Fields": {
      "main": [[{ "node": "Needs Clearbit?", "type": "main", "index": 0 }]]
    },
    "Needs Clearbit?": {
      "main": [
        [{ "node": "Clearbit Enrich", "type": "main", "index": 0 }],
        [{ "node": "Update HubSpot", "type": "main", "index": 0 }]
      ]
    },
    "Clearbit Enrich": {
      "main": [[{ "node": "Merge Clearbit Data", "type": "main", "index": 0 }]]
    },
    "Merge Clearbit Data": {
      "main": [[{ "node": "Needs PDL?", "type": "main", "index": 0 }]]
    },
    "Needs PDL?": {
      "main": [
        [{ "node": "PDL Enrichment", "type": "main", "index": 0 }],
        [{ "node": "Update HubSpot", "type": "main", "index": 0 }]
      ]
    },
    "PDL Enrichment": {
      "main": [[{ "node": "Merge PDL Data", "type": "main", "index": 0 }]]
    },
    "Merge PDL Data": {
      "main": [[{ "node": "Update HubSpot", "type": "main", "index": 0 }]]
    }
  },
  "pinData": {},
  "settings": { "executionOrder": "v1" },
  "staticData": null,
  "tags": [],
  "triggerCount": 0,
  "active": false,
  "meta": { "instanceId": "", "templateId": "revi-waterfall-enrich" }
}
