AutomationFlowsMarketing & Ads › Webhook Lead Responder via Gmail

Webhook Lead Responder via Gmail

Original n8n title: Inbound Lead Responder

Inbound Lead Responder. Uses httpRequest, gmail. Webhook trigger; 11 nodes.

Webhook trigger★★★★☆ complexity11 nodesHTTP RequestGmail
Marketing & Ads Trigger: Webhook Nodes: 11 Complexity: ★★★★☆ Added:

This workflow follows the Gmail → HTTP Request recipe pattern — see all workflows that pair these two integrations.

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": "Inbound Lead Responder",
  "nodes": [
    {
      "parameters": {
        "content": "## Inbound Lead Responder\n\n**What this does:**\nReceives inbound leads via webhook, classifies them with an LLM (OpenRouter / owl-alpha), and drafts personalized replies for high-value categories.\n\n**Flow:**\n1. Webhook receives POST /inbound-lead\n2. Normalize Lead extracts fields (with sample-data fallback)\n3. Classify Lead \u2192 category + urgency_score + intent_summary\n4. Parse Classification (try/catch JSON)\n5. Route by Category:\n   - spam / support \u2192 straight to Ack\n   - hot_lead / partnership \u2192 Draft Reply \u2192 Format \u2192 Gmail \u2192 Slack \u2192 Ack\n6. Acknowledge Webhook returns JSON status\n\n**Required env / creds:**\n- OpenRouter API (httpHeaderAuth)\n- Gmail OAuth2\n- SLACK_WEBHOOK_URL env var",
        "height": 520,
        "width": 460
      },
      "id": "sticky-doc-1",
      "name": "Workflow Docs",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -200,
        -40
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "inbound-lead",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-1",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        340,
        320
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "field-name",
              "name": "name",
              "value": "={{ $json.body?.name || \"Jordan Maxwell\" }}",
              "type": "string"
            },
            {
              "id": "field-email",
              "name": "email",
              "value": "={{ $json.body?.email || \"jordan@acmewidgets.com\" }}",
              "type": "string"
            },
            {
              "id": "field-message",
              "name": "message",
              "value": "={{ $json.body?.message || \"Hi \u2014 I run a 12-person agency and we're hitting a ceiling on growth. Our flywheel keeps stalling and we're losing roughly $40k MRR to churn each quarter. Curious if your team works with shops our size on retention systems. Open to a quick call next week.\" }}",
              "type": "string"
            },
            {
              "id": "field-source",
              "name": "source",
              "value": "={{ $json.body?.source || \"contact-form\" }}",
              "type": "string"
            },
            {
              "id": "field-company",
              "name": "company",
              "value": "={{ $json.body?.company || \"Acme Widgets\" }}",
              "type": "string"
            },
            {
              "id": "field-received-at",
              "name": "received_at",
              "value": "={{ $now.toISO() }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "normalize-1",
      "name": "Normalize Lead",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        560,
        320
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "HTTP-Referer",
              "value": "https://github.com/BiG-Zach/inbound-lead-responder-n8n"
            },
            {
              "name": "X-Title",
              "value": "Inbound Lead Responder"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"openrouter/owl-alpha\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You are a lead-classification assistant. Read the inbound message and return STRICT JSON only, no prose, no markdown fences. Schema: {\\\"category\\\": one of [\\\"hot_lead\\\", \\\"cold_lead\\\", \\\"support\\\", \\\"spam\\\", \\\"partnership\\\"], \\\"urgency_score\\\": integer 1-10, \\\"intent_summary\\\": short string under 200 chars}. hot_lead = clear buying intent or budget mentioned. cold_lead = vague curiosity. support = existing customer asking for help. spam = promotional/irrelevant. partnership = vendor/affiliate/collab outreach.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Name: \" + $json.name + \"\\nEmail: \" + $json.email + \"\\nCompany: \" + $json.company + \"\\nSource: \" + $json.source + \"\\nMessage:\\n\" + $json.message) }}\n    }\n  ],\n  \"temperature\": 0.2,\n  \"response_format\": {\"type\": \"json_object\"}\n}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "classify-1",
      "name": "Classify Lead",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        780,
        320
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Parse the OpenRouter classification response with a safe fallback.\nconst lead = $('Normalize Lead').item.json;\nconst raw = $input.item.json;\n\nlet category = 'cold_lead';\nlet urgency_score = 5;\nlet intent_summary = 'Unclassified \u2014 parser fallback.';\n\ntry {\n  const content = raw?.choices?.[0]?.message?.content;\n  if (!content) throw new Error('No content in LLM response');\n\n  // Strip possible markdown fencing just in case.\n  const cleaned = String(content).replace(/^```(?:json)?\\s*/i, '').replace(/```\\s*$/,'').trim();\n  const parsed = JSON.parse(cleaned);\n\n  if (parsed.category) category = String(parsed.category).toLowerCase().trim();\n  if (Number.isFinite(parsed.urgency_score)) {\n    urgency_score = Math.min(10, Math.max(1, Math.round(parsed.urgency_score)));\n  }\n  if (parsed.intent_summary) intent_summary = String(parsed.intent_summary).slice(0, 240);\n\n  const allowed = ['hot_lead','cold_lead','support','spam','partnership'];\n  if (!allowed.includes(category)) category = 'cold_lead';\n} catch (err) {\n  intent_summary = 'Classifier parse error: ' + (err?.message || 'unknown') + '. Defaulting to cold_lead.';\n}\n\nreturn [{\n  json: {\n    ...lead,\n    category,\n    urgency_score,\n    intent_summary\n  }\n}];"
      },
      "id": "parse-1",
      "name": "Parse Classification",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1000,
        320
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "cond-spam",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "spam",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "spam"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "cond-support",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "support",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "outputKey": "support"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": false,
                  "leftValue": "",
                  "typeValidation": "loose",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "cond-hot",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "hot_lead",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  },
                  {
                    "id": "cond-partnership",
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "partnership",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "or"
              },
              "outputKey": "high_priority"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "fallback"
        }
      },
      "id": "switch-1",
      "name": "Route by Category",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        1220,
        320
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "HTTP-Referer",
              "value": "https://github.com/BiG-Zach/inbound-lead-responder-n8n"
            },
            {
              "name": "X-Title",
              "value": "Inbound Lead Responder"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"openrouter/owl-alpha\",\n  \"messages\": [\n    {\n      \"role\": \"system\",\n      \"content\": \"You draft warm, personalized replies to inbound leads. Output ONLY the reply body \u2014 no subject line, no preamble, no quoted message. Constraints: exactly 3 sentences. Reference one specific detail from the lead's message. Friendly but not gushing. End with a clear next step. Sign off with the literal string [Your Name] on its own line.\"\n    },\n    {\n      \"role\": \"user\",\n      \"content\": {{ JSON.stringify(\"Lead name: \" + $json.name + \"\\nCompany: \" + $json.company + \"\\nCategory: \" + $json.category + \"\\nUrgency: \" + $json.urgency_score + \"/10\\nIntent: \" + $json.intent_summary + \"\\n\\nOriginal message:\\n\" + $json.message) }}\n    }\n  ],\n  \"temperature\": 0.7,\n  \"max_tokens\": 400\n}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "draft-1",
      "name": "Draft High-Priority Reply",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1480,
        220
      ],
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Merge the drafted reply with the lead context so downstream nodes have everything.\nconst lead = $('Parse Classification').item.json;\nconst draft = $input.item.json;\n\nlet body = '';\ntry {\n  body = draft?.choices?.[0]?.message?.content?.trim() || '';\n} catch (e) {\n  body = '';\n}\n\nif (!body) {\n  body = `Hi ${lead.name || 'there'},\\n\\nThanks for reaching out \u2014 we'd love to learn more about what you're working on. Mind if I send over a couple of times for a quick call this week?\\n\\n[Your Name]`;\n}\n\nconst source = lead.source || 'your inquiry';\nconst subject = `Re: your message about ${source}`;\n\nreturn [{\n  json: {\n    ...lead,\n    reply_subject: subject,\n    reply_body: body,\n    to_email: lead.email,\n    to_name: lead.name\n  }\n}];"
      },
      "id": "format-1",
      "name": "Format Reply",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1700,
        220
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to_email }}",
        "subject": "={{ $json.reply_subject }}",
        "emailType": "text",
        "message": "={{ $json.reply_body }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "gmail-1",
      "name": "Send Email Reply",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1920,
        220
      ],
      "continueOnFail": true,
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.SLACK_WEBHOOK_URL }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"text\": {{ JSON.stringify(\"\ud83d\udd25 New \" + $json.category + \" (urgency \" + $json.urgency_score + \"/10) from \" + $json.name + \" <\" + $json.email + \">\\n*Intent:* \" + $json.intent_summary + \"\\n*Draft sent:* \" + $json.reply_subject) }}\n}",
        "options": {
          "timeout": 15000
        }
      },
      "id": "slack-1",
      "name": "Notify Slack",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2140,
        220
      ],
      "continueOnFail": true
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"status\": \"received\",\n  \"category\": \"{{ $json.category }}\",\n  \"urgency\": {{ $json.urgency_score }}\n}",
        "options": {}
      },
      "id": "respond-1",
      "name": "Acknowledge Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        2360,
        320
      ]
    }
  ],
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "Normalize Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Lead": {
      "main": [
        [
          {
            "node": "Classify Lead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Classify Lead": {
      "main": [
        [
          {
            "node": "Parse Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Classification": {
      "main": [
        [
          {
            "node": "Route by Category",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Category": {
      "main": [
        [
          {
            "node": "Acknowledge Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Acknowledge Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Draft High-Priority Reply",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Acknowledge Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Draft High-Priority Reply": {
      "main": [
        [
          {
            "node": "Format Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Reply": {
      "main": [
        [
          {
            "node": "Send Email Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Email Reply": {
      "main": [
        [
          {
            "node": "Notify Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Slack": {
      "main": [
        [
          {
            "node": "Acknowledge Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

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

About this workflow

Inbound Lead Responder. Uses httpRequest, gmail. Webhook trigger; 11 nodes.

Source: https://github.com/BiG-Zach/inbound-lead-responder-n8n/blob/main/workflow.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

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
Marketing & Ads

A comprehensive n8n workflow template for streamlining influencer application processing with real-time social media data validation, intelligent scoring algorithms, and automated onboarding workflows

N8N Nodes Verifiemail, Stop And Error, HTTP Request +2
Marketing & Ads

AI Lead Qualification & Follow-Up. Uses httpRequest, slack, googleSheets, gmail. Webhook trigger; 18 nodes.

HTTP Request, Slack, Google Sheets +2
Marketing & Ads

Smart influencer discovery: Automatically finds and qualifies influencers based on your criteria and target audience Automated outreach: Sends personalized collaboration proposals with dynamic pricing

HTTP Request, Gmail, Google Sheets