{
  "nodes": [
    {
      "id": "sticky-title",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -880,
        -440
      ],
      "parameters": {
        "content": "## Every lead source into one CRM\n- Facebook ads, the website form, and Google lead forms all end up as one clean contact in GoHighLevel, with every lead logged in Airtable\n- Repeat enquiries update the contact already on file, and the sales team gets a Slack message either way",
        "height": 200,
        "width": 560,
        "color": 5
      }
    },
    {
      "id": "sticky-sources",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1200,
        -200
      ],
      "parameters": {
        "content": "## Three ways in, one path\n- Facebook, the website, and Google each post to their own door here\n- Past this point every lead gets treated exactly the same",
        "height": 780,
        "width": 340,
        "color": 4
      }
    },
    {
      "id": "webhook-facebook",
      "name": "Facebook lead arrives",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1120,
        -40
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "demo/lead-facebook",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "webhook-website",
      "name": "Website form lead arrives",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1120,
        180
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "demo/lead-website",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "webhook-google",
      "name": "Google lead arrives",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1120,
        400
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "demo/lead-google",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "code-read-lead",
      "name": "Read the lead details",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -820,
        180
      ],
      "parameters": {
        "jsCode": "// Three sources post here. Work out which one sent the lead and pull the same clean details out every time.\nconst raw = $json.body ?? $json;\nconst url = String($json.webhookUrl || '');\n\nconst clean = (v) => (v === undefined || v === null ? '' : String(v).trim());\nconst pick = (map, ...keys) => {\n  for (const k of keys) {\n    const v = clean(map[k]);\n    if (v) return v;\n  }\n  return '';\n};\n\nconst lead = {\n  name: '',\n  phone: '',\n  email: '',\n  service: '',\n  message: '',\n  lead_source: 'website',\n};\n\n// Facebook Lead Ads sends the answers as a field_data list\nconst fbFields =\n  raw?.entry?.[0]?.changes?.[0]?.value?.field_data ??\n  raw?.field_data ??\n  null;\n\n// Google lead forms (and Zapier forwards of them) send a user_column_data list\nconst gFields = raw?.user_column_data ?? raw?.lead?.user_column_data ?? null;\n\nif (Array.isArray(fbFields)) {\n  lead.lead_source = 'facebook';\n  const map = {};\n  for (const f of fbFields) {\n    map[clean(f?.name).toLowerCase()] = clean((f?.values || [])[0]);\n  }\n  lead.name =\n    pick(map, 'full_name', 'name') ||\n    [pick(map, 'first_name'), pick(map, 'last_name')].filter(Boolean).join(' ');\n  lead.email = pick(map, 'email');\n  lead.phone = pick(map, 'phone_number', 'phone');\n  lead.service = pick(map, 'service', 'what_do_you_need_help_with', 'job_type');\n  lead.message = pick(map, 'message', 'comments', 'details');\n} else if (Array.isArray(gFields)) {\n  lead.lead_source = 'google';\n  const map = {};\n  for (const c of gFields) {\n    const key = clean(c?.column_id || c?.column_name).toLowerCase();\n    if (key && !map[key]) map[key] = clean(c?.string_value);\n  }\n  lead.name =\n    pick(map, 'full_name', 'name') ||\n    [pick(map, 'first_name'), pick(map, 'last_name')].filter(Boolean).join(' ');\n  lead.email = pick(map, 'email', 'user_email');\n  lead.phone = pick(map, 'phone_number', 'user_phone', 'phone');\n  lead.service = pick(map, 'service', 'which_service_do_you_need', 'job_type');\n  lead.message = pick(map, 'message', 'comments', 'details');\n} else {\n  // Website form, either flat fields or wrapped in a data object\n  const flat = raw?.data && typeof raw.data === 'object' ? raw.data : raw;\n  const map = {};\n  for (const [k, v] of Object.entries(flat || {})) {\n    map[clean(k).toLowerCase()] = clean(v);\n  }\n  lead.name =\n    pick(map, 'name', 'full_name', 'fullname') ||\n    [pick(map, 'first_name', 'firstname'), pick(map, 'last_name', 'lastname')]\n      .filter(Boolean)\n      .join(' ');\n  lead.email = pick(map, 'email', 'email_address');\n  lead.phone = pick(map, 'phone', 'phone_number', 'mobile', 'tel');\n  lead.service = pick(map, 'service', 'service_needed', 'job_type');\n  lead.message = pick(map, 'message', 'comments', 'details', 'notes');\n  // A forwarded Google lead can arrive flat too, so let the door it came through decide\n  if (url.includes('lead-google')) lead.lead_source = 'google';\n  if (url.includes('lead-facebook')) lead.lead_source = 'facebook';\n}\n\nif (!lead.name) lead.name = 'New lead';\n\n// Phone down to just digits with a leading +\nlet digits = lead.phone.replace(/[^0-9]/g, '');\nif (digits.length === 10) digits = '1' + digits;\nlead.phone = digits ? '+' + digits : '';\n\nconst tagBySource = {\n  facebook: 'fb-lead',\n  website: 'website-lead',\n  google: 'google-lead',\n};\nlead.source_tag = tagBySource[lead.lead_source];\nlead.receivedAt = new Date().toISOString();\n\nreturn [{ json: lead }];"
      }
    },
    {
      "id": "http-ghl-check",
      "name": "Check GoHighLevel for this customer",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -580,
        180
      ],
      "parameters": {
        "method": "GET",
        "url": "https://services.leadconnectorhq.com/contacts/",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "locationId",
              "value": "bsHomeSvcDemo01"
            },
            {
              "name": "query",
              "value": "={{ $json.phone || $json.email }}"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Version",
              "value": "2021-07-28"
            }
          ]
        },
        "options": {}
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "if-existing",
      "name": "Already in GoHighLevel?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -340,
        180
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "has-contact",
              "leftValue": "={{ ($json.contacts || []).length }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "http-ghl-update",
      "name": "Update the customer we already have",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -80,
        60
      ],
      "parameters": {
        "method": "POST",
        "url": "https://services.leadconnectorhq.com/contacts/upsert",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Version",
              "value": "2021-07-28"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ locationId: 'bsHomeSvcDemo01', name: $('Read the lead details').item.json.name, phone: $('Read the lead details').item.json.phone, email: $('Read the lead details').item.json.email, source: $('Read the lead details').item.json.lead_source, tags: [$('Read the lead details').item.json.source_tag, 'repeat-enquiry'] }) }}",
        "options": {}
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "http-ghl-create",
      "name": "Save the new lead in GoHighLevel",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -80,
        320
      ],
      "parameters": {
        "method": "POST",
        "url": "https://services.leadconnectorhq.com/contacts/upsert",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Version",
              "value": "2021-07-28"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ locationId: 'bsHomeSvcDemo01', name: $('Read the lead details').item.json.name, phone: $('Read the lead details').item.json.phone, email: $('Read the lead details').item.json.email, source: $('Read the lead details').item.json.lead_source, tags: [$('Read the lead details').item.json.source_tag, 'new-lead'] }) }}",
        "options": {}
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "airtable-log",
      "name": "Log the lead in Airtable",
      "type": "n8n-nodes-base.airtable",
      "typeVersion": 2.1,
      "position": [
        180,
        180
      ],
      "parameters": {
        "authentication": "airtableTokenApi",
        "resource": "record",
        "operation": "create",
        "base": {
          "__rl": true,
          "mode": "id",
          "value": "appBlueSummit001"
        },
        "table": {
          "__rl": true,
          "mode": "id",
          "value": "tblLeads"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Name": "={{ $('Read the lead details').item.json.name }}",
            "Phone": "={{ $('Read the lead details').item.json.phone }}",
            "Email": "={{ $('Read the lead details').item.json.email }}",
            "Source": "={{ $('Read the lead details').item.json.lead_source }}",
            "Service": "={{ $('Read the lead details').item.json.service }}",
            "Status": "New"
          },
          "matchingColumns": [],
          "schema": []
        },
        "options": {}
      },
      "credentials": {
        "airtableTokenApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "slack-notify",
      "name": "Tell the sales team in Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.3,
      "position": [
        420,
        180
      ],
      "parameters": {
        "resource": "message",
        "operation": "post",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "value": "#sales-team",
          "mode": "name"
        },
        "text": "=New lead: {{ $('Read the lead details').item.json.name }}\nCame from: {{ $('Read the lead details').item.json.lead_source }}\nWants: {{ $('Read the lead details').item.json.service || 'not sure yet' }}\nPhone: {{ $('Read the lead details').item.json.phone || 'no number given' }}",
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "id": "respond-ok",
      "name": "Tell the lead source it landed",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        660,
        180
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ saved: true, source: $('Read the lead details').item.json.lead_source }) }}",
        "options": {}
      }
    }
  ],
  "connections": {
    "Facebook lead arrives": {
      "main": [
        [
          {
            "node": "Read the lead details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Website form lead arrives": {
      "main": [
        [
          {
            "node": "Read the lead details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google lead arrives": {
      "main": [
        [
          {
            "node": "Read the lead details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read the lead details": {
      "main": [
        [
          {
            "node": "Check GoHighLevel for this customer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check GoHighLevel for this customer": {
      "main": [
        [
          {
            "node": "Already in GoHighLevel?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Already in GoHighLevel?": {
      "main": [
        [
          {
            "node": "Update the customer we already have",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Save the new lead in GoHighLevel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update the customer we already have": {
      "main": [
        [
          {
            "node": "Log the lead in Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save the new lead in GoHighLevel": {
      "main": [
        [
          {
            "node": "Log the lead in Airtable",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log the lead in Airtable": {
      "main": [
        [
          {
            "node": "Tell the sales team in Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Tell the sales team in Slack": {
      "main": [
        [
          {
            "node": "Tell the lead source it landed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}