2026
AI lead scoring that never drops a lead
An intake pipeline that turns raw form submissions into scored, structured leads in a spreadsheet, and pings Slack the moment a hot one arrives.
The problem
Most small teams collect leads in a form and then read them by hand. Someone opens the inbox, decides who looks serious, retypes the details into a sheet and, on a busy day, forgets a few. The cost is not the typing. It is that the best lead sits unread for six hours.
What I built
A webhook receives the form submission. The payload is normalised, then passed to a language model that returns a strict JSON object: a score from one to five, a short reason, a detected language and a needs_review flag. The row is appended to a spreadsheet, and anything scoring four or higher triggers an immediate Slack message in a dedicated channel.
Everything runs on native n8n nodes. No code nodes, no HTTP nodes gluing things together, because a workflow a client can read is a workflow a client can maintain.
Making the model output trustworthy
A language model will occasionally return prose instead of JSON, or invent a field. Two layers handle that:
- A structured output parser with an explicit JSON schema rejects anything that does not match.
- When it rejects, an auto fix model gets one attempt to repair its own output against the same schema.
If both fail, the lead is still written to the sheet, flagged needs_review, and a warning lands in Slack. A lead is never silently dropped, which is the only failure mode that actually costs money.
The parts that were genuinely hard
Concurrent writes overwrote each other. The Google Sheets node defaults to reading the last row and updating it. Two leads arriving in the same second produced one row. Switching the node to true append mode fixed it, and a test sending three simultaneous submissions now verifies exactly three rows.
Retries multiplied the data. Turning on retry for the Sheets node made n8n write every row three times and stretched a 2.5 second run to 16 seconds. Retry belongs on calls that can fail transiently, not on an append that already succeeded.
A bot check blocked my own tests. The test script was rejected by Cloudflare’s browser integrity check until it sent a realistic user agent. Worth knowing before you debug the workflow that was never the problem.
Reliability, not features
The build ships with a dedicated error workflow. Any failure anywhere in the pipeline sends a Slack alert with the workflow name and the failing node, so a broken automation announces itself instead of quietly going dark.
A small Next.js dashboard behind password protection shows the last fifty leads, so nobody has to open the spreadsheet to answer “did anything come in today”.