A small, production-minded n8n lead pipeline. A web form submission is enriched, scored against editable rules, written to Airtable as the system of record, and pushed to Slack for the team.
It runs end to end on mock data — the enrichment provider (Apollo) is simulated so the whole thing works without a single credential, and so you can watch what happens when that provider fails. Airtable and Slack are real.
The point of this repo isn't "a workflow that works on the happy path." Most demos do. The point is the three things that quietly break these pipelines in production — and how each one is handled here.
flowchart LR
A[Web form] --> B[Normalize + idempotency key]
B --> C{Already in Airtable?}
C -- yes --> D[No-op · 0 rows · 0 alerts]
C -- no --> E[Enrich via Apollo]
E -- ok --> F[Score from config]
E -- down / rate-limited --> G[Keep lead · score = null · needs review]
F --> H[Save to Airtable]
G --> H
H --> I[Notify Slack with reason]
| Failure mode | What most workflows do | What this one does |
|---|---|---|
| Same lead submits twice | Two Airtable rows; sales calls them twice | Idempotency key on email → one row, the second submit is a no-op |
| Apollo down / rate-limited | Lead lost in a red node | Lead is still captured, flagged needs review, alert fires — a lead never disappears into a broken step |
| Unknown company | Guessed or empty score | Score stays null and routes to a human — never fabricate a score from missing data |
scoring_config.json holds size bands, industry bonus,
and hot/warm thresholds. Your team edits this file; the workflow nodes are never
touched.
{
"size_bands": [
{ "min": 500, "points": 40, "label": "enterprise" },
{ "min": 50, "points": 25, "label": "mid-market" },
{ "min": 10, "points": 10, "label": "small" },
{ "min": 0, "points": 0, "label": "micro" }
],
"industry_bonus": { "industrial automation": 20, "computer software": 15 },
"hot_threshold": 50,
"warm_threshold": 25
}cp .env.example .env # fill in your Airtable PAT + base/table + Slack webhook
./render.sh # inject secrets into a runnable workflow (gitignored)
python3 mock_apollo_server.py & # mock Apollo on :8899 (same JSON shape as the real API)
docker start n8n-demo # n8n on :5678
# import n8n_workflow.runnable.json in the n8n UIbash run_demo.sh check # all services up? (expect 200s)
bash run_demo.sh fresh # clear the Airtable table
bash run_demo.sh 1 # a good lead → HOT, saved, Slack notified
bash run_demo.sh 2 # the same lead again → no-op (dedup)
bash run_demo.sh 3 # Apollo goes down → lead survives, flagged for review
bash run_demo.sh table # final state: three submits, two rowsPrefer clicking? python3 demo_web.py serves a plain-English control panel on
http://localhost:8877 — one button per moment, with the live Airtable table
below.
curl localhost:8899/_fault/on # Apollo now returns 429
# submit a lead → it still lands, flagged, and alerts
curl localhost:8899/_fault/off| File | What it is |
|---|---|
n8n_workflow.json |
The workflow, sanitized (secrets are __PLACEHOLDERS__) |
render.sh |
Injects .env values into n8n_workflow.runnable.json (gitignored) |
scoring_config.json |
The editable scoring rules |
mock_apollo_server.py |
Credential-free Apollo mock, with a /_fault switch |
demo_step.py |
The pipeline logic as a CLI (good / dup / fault / table) |
demo_web.py |
Web control panel for a non-technical viewer |
run_demo.sh, fire.sh |
Trigger helpers (run_demo.sh = logic; fire.sh = the n8n test webhook) |
wipe_airtable.py |
Clears the table between takes |
MIT — see LICENSE.
Author: Bohea — independent industrial software engineer (Shenzhen). Operator HMIs · device integration · machine data into your customer's ERP. More runnable demos & engineering notes on the site.