n8n Webhook Routing: Conditional Logic That Actually Works (No Coding Required)
TL;DR: Most n8n users blast all webhook data into one workflow and chaos ensues. The fix? Use a Router node to conditionally split data based on conditions (lead vs. customer vs. error). One webhook, multiple destinations, zero code.
The Problem: "Webhook Chaos"
You've set up a webhook. Maybe it's Typeform submissions, Stripe events, or Calendly bookings. Now you realize: the same webhook endpoint receives different types of data, and you need to handle them differently.
Real scenario: You're tracking form submissions via Typeform. The form has 3 different pages depending on user input:
- Page 1: Lead capture (name + email)
- Page 2: Customer survey (existing customer feedback)
- Page 3: Error log (customer report a bug)
All three send to the same webhook URL.
Without routing, your workflow becomes spaghetti:
Webhook trigger
↓ Check type (if/else/if/else/if/else)
├─ If lead: send Slack message
├─ If customer: add to CRM
└─ If error: create ticket + alert team
With nested conditions, it's unmaintainable. With routing, it's clean.
Solution: The Router Node
The Router node is n8n's secret weapon for splitting data streams. It evaluates conditions and sends data to the correct output branch automatically.
Syntax is simple:
If condition → output branch 1
Else if condition → output branch 2
Else → default output
Step-by-Step: Typeform Webhook Routing
Setup: Three Typeform Workflows
You have one Typeform and three potential outcomes. You need three separate branches in n8n, not three separate workflows.
Why? A single workflow with routing is cleaner than 3 webhooks / 3 workflows.
Step 1: Webhook Trigger
Trigger: Webhook
Webhook URL: https://your-n8n.com/webhook/typeform-router
Method: POST
When Typeform submits data, it hits this webhook. The webhook returns the form response object, which includes:
-
answers[]— array of user responses -
form_id— Typeform ID -
response_id— unique response ID
For this example, assume Typeform is configured to send a "form_type" field in the JSON:
{
"form_id": "abc123",
"response_id": "resp_xyz",
"form_type": "lead_capture",
"answers": [
{ "field_id": "q1", "text": "John Doe" },
{ "field_id": "q2", "email": "john@example.com" }
]
}
Step 2: Add Router Node
After the Webhook trigger, add a Router node.
Router node settings:
-
Output 1: Condition =
{{ $json.form_type === "lead_capture" }} -
Output 2: Condition =
{{ $json.form_type === "customer_survey" }} -
Output 3: Condition =
{{ $json.form_type === "error_report" }} - Default output: (anything that doesn't match)
Step 3: Add Nodes per Branch
Now you have 3 output branches from the Router. Connect each to its own action:
Branch 1 (Lead Capture):
Router Output 1
↓
Format Slack message
↓
Send Slack notification to #leads channel
Branch 2 (Customer Survey):
Router Output 2
↓
Extract customer feedback
↓
Add to HubSpot as activity
↓
Send thank-you email (Resend/Brevo)
Branch 3 (Error Report):
Router Output 3
↓
Create GitHub issue with error details
↓
Send alert to #urgent-bugs Slack channel
Real Code Example: Airtable Lead Routing
Let's say you're syncing leads from Typeform → Airtable, but different fields matter for different lead types.
Scenario:
- B2B leads get tagged in Airtable with "Enterprise" (use in HubSpot later)
- B2C leads get auto-replied immediately (send email)
- Inbound support requests go to a separate table
Webhook → Router → Conditional Branches:
Webhook (Typeform)
↓
Router
├─ Output 1: $json.business_type === "B2B"
│ └─ Add to Airtable [Leads] with tag "Enterprise"
│ └─ (Optional) Webhook to HubSpot to notify sales team
│
├─ Output 2: $json.business_type === "B2C"
│ └─ Add to Airtable [Leads] with tag "Consumer"
│ └─ Send auto-reply email
│
└─ Output 3: $json.request_type === "support"
└─ Add to Airtable [Support Tickets]
└─ Assign to support team Slack channel
Each branch is independent. No interference, no spaghetti logic.
Why SMBs Need Webhook Routing
Real pain point: You're managing 50+ leads per day from multiple sources (Typeform, LinkedIn, website contact form, referrals). Without routing:
- Leads end up in wrong fields
- Follow-ups go to wrong people
- Duplicate entries in CRM
- Sales team misses half the leads
With routing: Each lead type flows to its correct destination. No manual sorting. No "oops, missed that one."
Advanced: Routing by HTTP Header or URL Parameter
Sometimes Typeform (or other services) don't include a type field in the JSON. You can route based on:
Option 1: Different Webhook URLs (cleanest)
https://your-n8n.com/webhook/typeform-leads
https://your-n8n.com/webhook/typeform-support
https://your-n8n.com/webhook/typeform-survey
Then each webhook feeds into its own workflow. (But this defeats the purpose of a single Router.)
Option 2: Query Parameter
https://your-n8n.com/webhook/typeform?type=lead_capture
Access in n8n: {{ $url.query.type }}
Then route on: {{ $url.query.type === "lead_capture" }}
Option 3: HTTP Header
Typeform can send a custom header: X-Form-Type: lead_capture
Access in n8n: {{ $headers.get('x-form-type') }}
Then route on: {{ $headers.get('x-form-type') === "lead_capture" }}
Common Gotchas & Fixes
Gotcha 1: Condition returns wrong type
$json.age === 21 // ✓ correct
$json.age == "21" // ✗ wrong (string vs number)
Always use === (strict equality). Check data types in the test panel.
Gotcha 2: Router has no output branches
If all conditions fail → Router output is empty → workflow stops.
Fix: Always add a default output to catch unmatched data. Log it or send to a fallback workflow.
Gotcha 3: Nested Router nodes (routing → routing → routing)
Router 1
├─ Output 1 → Router 2
│ ├─ Output 1 → Action A
│ └─ Output 2 → Action B
└─ Output 2 → Action C
This works, but it's hard to debug. Use flat routing when possible.
Case Study: Slack Webhook Routing
Real example: A team uses Slack for everything (sales leads, support tickets, billing alerts). Without routing, all notifications blend together. No priority. Everything feels urgent.
Solution: Webhook routing + Slack channel assignment
Incoming webhook (could be from Zapier, IFTTT, custom API)
↓
Router (check message priority)
├─ HIGH priority (payment failure, security alert)
│ └─ Send to #urgent-alerts (red color, @channel ping)
│
├─ MEDIUM priority (new lead, customer reply)
│ └─ Send to #sales (yellow color, normal ping)
│
└─ LOW priority (form submission, feedback)
└─ Send to #feedback (blue color, no ping)
Result: Slack channels stay organized. People see what matters first. Alerts don't drown in noise.
Next Steps: Level Up Your Routing
After you master basic routing, try:
- Routing + Batch: Collect 10 leads, batch them, then route the batch
- Routing + Error Handling: Route successful/failed API responses separately
- Routing + Database Lookups: Route based on whether a lead exists in Airtable (existing vs. new)
Takeaway
Webhook routing is the difference between:
- ❌ One chaotic workflow that handles 5 different things badly
- ✅ Five clean branches that each do one thing well
If you're managing multiple data sources or lead types, Router is non-negotiable.
Ready to Build This?
The $99 audit includes:
- Mapping your current data sources → n8n routing strategy
- Identifying which conditions matter (business type? priority? source?)
- Building your first Router workflow (15–30 min per source)
The $299/mo retainer includes:
- Ongoing workflow tuning as your data sources change
- Performance optimization (batch vs. real-time routing)
- Support when conditions need tweaking
Get a free audit or book a retainer.
Questions? Drop a comment below or check out n8n's Router docs.
Top comments (0)