On this page
This n8n speed-to-lead workflow answered a web form submission in 2.4 seconds and 1.9 seconds, measured on 2026-09-17. Anthropic Claude Haiku 4.5 drafts the first text, Claude Opus 5 reads the homeowner's reply, qualifies the job and proposes a visit window, and every state change is written to Postgres. The demo is simulated and the JSON is MIT-licensed.
The advice every home-services owner has already heard is a checklist: call within five minutes, text within ten, never let a form sit overnight. Nobody explains how you do that at 11pm on a Sunday, or during a job, or while you are on a roof. The five minutes is not the hard part. Being awake for it is.
Episode 02 of the n8n Workflows series makes the reply itself the automation rather than the rule. A homeowner fills a form and a text is already written and saved before the page has finished loading. They reply. A second, larger model reads what they said, decides whether it knows enough to book, and proposes a real visit window. The decision arrives as a bare JSON object, not as prose somebody has to parse. Nothing in the chain is allowed to invent a price, a warranty, an arrival time or a technician name.
If iframes are blocked where you are reading this, the video is at youtu.be/-lEQYzTgI5o.
Specifications
| Item | Value |
|---|---|
| Workflow name | Speed-to-Lead (simulated demo) |
| Nodes | 17 |
| Triggers |
POST /webhook/demo/lead,
POST /webhook/demo/reply,
GET /webhook/demo/state?token=
|
| First-reply model | Anthropic Claude Haiku 4.5, capped at 200 output tokens |
| Qualifying model | Anthropic Claude Opus 5, capped at 300 output tokens |
| Storage |
Postgres: demo_sessions, demo_messages,
demo_bookings
|
| Messaging | None. No SMS provider, no CRM contact, no send queue. |
| Session key |
md5(random || clock_timestamp()), generated in SQL rather
than in the workflow
|
| Public JSON includes |
All 17 nodes, both system prompts, every SQL statement and the
slot-mapping code. Credentials stripped to REPLACE_ME, host
examples read YOUR-N8N.
|
| Licence | MIT |
How does the n8n speed-to-lead workflow reply in about two seconds?
It skips everything that normally sits between a form and a human. The webhook receives the form, one Code node cleans it, one SQL statement creates the session and returns a token, and one Anthropic call drafts a text of under 320 characters. The reply is saved to Postgres and returned in the same HTTP response, so the page can draw it immediately.
Lane one is the new lead. Clean Lead treats the body as hostile,
because the endpoint is public: it strips control characters, clamps the name,
phone, city and issue to fixed lengths, falls back to placeholders where a
field is empty, and returns a 400 if there is no issue left after cleaning.
Create Session inserts the row and returns the token.
AI First Text casts Claude Haiku 4.5 as the dispatcher for a demo
HVAC company and instructs it to use the first name, reference the stated
problem, ask exactly one qualifying question, and never invent prices,
warranties, technician names or arrival times.
Lane two is the homeowner's reply. Clean Reply whitelists the
token to hex characters and clamps the text to 400 characters.
Log Lead Text writes the new message and builds the transcript.
AI Qualify + Book hands that transcript to Claude Opus 5 with one
instruction that makes the whole lane deterministic: reply with bare JSON
containing a message, a boolean for whether to book, a job type and a slot
hint. The slot hint is one of four tokens, never a raw date.
Parse Decision turns that hint into both a machine timestamp and
a finished human string, and Save AI Reply writes the message,
conditionally inserts the booking, and stamps the session the first time a
booking happens.
Lane three is the page polling its own state by token, one Postgres query returning the message list and any booking as a single JSON object.
| Lane | Node | What it does |
|---|---|---|
| Lead | Web Lead In |
Webhook, POST /demo/lead. Accepts name, phone, city, issue.
|
| Lead | Clean Lead |
Code. Strips control chars, clamps every field, 400s on an empty issue. |
| Lead | Create Session |
Postgres INSERT returning id, token, name, city, issue. Token generated in SQL. |
| Lead | AI First Text |
Anthropic Claude Haiku 4.5. Under 320 characters, exactly one qualifying question. |
| Lead | Save AI Text, Respond Lead |
Writes the message, returns ok, token and message to the page. |
| Reply | Homeowner Reply In, Clean Reply |
Webhook plus a token whitelist and a 400-character clamp. |
| Reply | Log Lead Text |
One statement: insert the message, then build the transcript by aggregation plus an explicit append. See the gotcha. |
| Reply | AI Qualify + Book |
Anthropic Claude Opus 5. Returns bare JSON: message, book, job_type, slot_hint. |
| Reply | Parse Decision |
Code. Maps the slot hint to a day plus a fixed four-hour window, renders the label server-side. |
| Reply | Save AI Reply, Respond Reply |
Message plus at most one booking per session, then the JSON response. |
| State |
State In, Read State,
Respond State
|
One Postgres query by token returning messages and booking as JSON. |
What is real in the public JSON and what is a placeholder?
Both system prompts, every SQL statement, the slot mapping and all 17 nodes
are exactly what ran. Two credentials ship as REPLACE_ME: a
Postgres credential and a header-auth credential carrying the Anthropic key as
x-api-key. The curl examples read YOUR-N8N, which is
your own instance hostname.
Two further things you should know before importing:
-
The model names come from environment variables with hardcoded
fallbacks.
CLASSIFIER_MODELdefaults toclaude-haiku-4-5andCLAUDE_MODEL_SONNETdefaults toclaude-opus-5. The second variable name is misleading and the fallback is genuinely Opus 5. Rename the variable or edit the fallback string if you want a different model on that lane. -
The column types in
schema.sqlwere derived from the SQL, not exported from a live database. They are sensible defaults rather than a verified dump, and the file says so. The unique index ondemo_sessions.tokenis load-bearing: every lane after the first looks a session up by token. - The demo page is not in the repo. It is a static page making exactly three fetch calls against the contract above. Build your own.
How fast was it, measured?
Submit to first reply was 2.4 seconds and 1.9 seconds. Reply to booking was 4.35 seconds and 3.37 seconds. All four figures were observed on 2026-09-17 across n8n executions 25090, 25091, 25095 and 25096.
Those are four measurements on one instance, on one day, against one Anthropic account. They are not a benchmark and they are not a promise about your stack. Latency here is dominated by the model call, so it will move with the model you choose, your region, and how busy the provider is at that moment. The Postgres writes are not the expensive part.
Which build gotchas cost a failed run?
A data-modifying CTE is invisible to the rest of the same statement
This is the one worth the read, because it produced a bug that looked like a model failure and was not.
Log Lead Text runs a single SQL statement. A CTE inserts the
homeowner's new message into demo_messages, and the final SELECT
in that same statement builds the transcript by aggregating rows from
demo_messages. The intuition is that the row inserted a moment
ago is now there to be aggregated. It is not. In Postgres, every CTE in a
statement sees the same snapshot of the table, taken when the statement
started, so the row the CTE just inserted does not exist as far as the
aggregation is concerned.
The visible symptom: the workflow qualified the lead against a transcript missing the customer's most recent message. It answered the message before the one the homeowner had just sent. Every log looked healthy. The model was behaving correctly on the input it was given, and the input was one message stale.
The fix is not a second CTE and not a smarter query. It is splitting the write
from the read: append the new line to the aggregated transcript explicitly,
with || E'\nCustomer: ' || $2 on the end. If you refactor that
statement and drop the explicit append because it looks redundant, the bug
comes straight back, and it will not announce itself.
The backend ships a finished slot label and the browser must not re-derive it
Parse Decision produces two things from the same Date object:
slot_at, a UTC timestamp for the machine, and
slot_label, a finished human string such as "Tomorrow, Sep 18 -
8:00am - 12:00pm". If a page instead formats slot_at itself in
the browser, timezone conversion can shift the hour across the boundary the
model chose, so an 8am to 12pm window renders as 12pm to 4pm and the chat
bubble and the booking card visibly disagree about when someone is coming.
Render slot_label as given. Never recompute it client-side.
Parse Decision salvages JSON out of a misbehaving model
The system prompt asks for bare JSON with no markdown fence. Nothing enforces
that. The Code node tries a direct parse, falls back to extracting the first
brace-delimited block out of prose, and finally falls back to a generic
message with book: false rather than throwing. The homeowner is
holding a phone waiting for a reply; a thrown error in that path is a dead
webhook response, which is a worse outcome than a generic answer.
The webhooks are public, so the inputs are clamped
There is no auth in front of any of the three endpoints. Every field is length-capped and control-character-stripped before it reaches Postgres or a model. That keeps abuse cheap rather than impossible. If you fork this for a real intake flow, add rate limiting before you add anything else.
How do I run the speed-to-lead workflow myself?
Import the free workflow JSON (MIT) from
github.com/waseemnasir2k26/n8n-workflows/tree/main/workflows/02-speed-to-lead, run schema.sql against a Postgres database, create the two
credentials, and POST to the lead webhook.
What you have to supply:
-
A Postgres database, with
schema.sqlapplied first. The workflow writes only to its own three tables. -
An Anthropic API key, as a header-auth credential named
x-api-key. Theanthropic-versionheader is already a literal parameter on both nodes and needs no credential. - A demo page of your own, if you want to see it as a conversation rather than as JSON in a terminal.
On running costs, in general terms: there are exactly two metered calls per conversation, one short Anthropic call for the first text and one for the qualifying decision, both output-token capped. There is no media generation and no other external API. The n8n instance and the Postgres database are infrastructure you host. Your provider dashboard is the only honest source for what that costs you, so check it rather than trusting anybody's arithmetic, including mine.
Frequently asked questions
How fast can an AI reply to a web form lead?
In this workflow, submit to first reply was 2.4 seconds and 1.9 seconds, measured on 2026-09-17 across n8n executions 25090, 25091, 25095 and 25096. Latency is dominated by the model call rather than by the database, so your figures will move with the model, the region and the provider load.
Is Lone Star Air a real company?
No. Lone Star Air is a fictional company invented for this demo, and every page of the public demo is labelled SIMULATED. No SMS provider is wired in: the text message is a JSON string returned to the caller for a page to render as a chat bubble. Treat the whole thing as a simulation.
Why does the workflow use two different Claude models?
Because the two jobs are not the same job. The first text is short, formulaic and latency-critical, so it runs on Anthropic Claude Haiku 4.5 capped at 200 output tokens. Qualifying a reply and choosing a visit window is a judgement call, so it runs on Claude Opus 5 capped at 300 output tokens.
Why did the workflow answer the wrong message?
Because a data-modifying CTE is invisible to the rest of the same SQL statement. The CTE inserted the newest message while the aggregation in the same statement still saw the pre-statement snapshot, so the transcript was one message stale. The fix is to append the new line explicitly rather than relying on the insert being visible.
What do I need to run this n8n workflow?
Two credentials and one database: a Postgres credential with
schema.sql already applied, and a header-auth credential carrying
your Anthropic API key as x-api-key. Both ship as
REPLACE_ME in the public JSON. The demo page is not included and
you build your own against the three webhook endpoints.
The rest of the series
One workflow per video, the JSON given away under MIT each time, built by Waseem Nasir at SkynetLabs. The other three published episodes:
- Episode 01, n8n Shorts Factory: one topic string in, a captioned 9:16 Short out, with the publish step deliberately switched off.
- Episode 03, n8n Maps lead harvest: a trade and a city in, a deduped lead table out, keyed on place id, written to Postgres.
- Episode 04, n8n freight quote parser: a quote email in, eight validated fields in Postgres out, with the reply left as a draft.
WhatsApp +92 300 1001957 · Waseem Nasir, SkynetLabs
Hire SkynetLabs, our Top Rated agency on Fiverr: https://www.fiverr.com/agencies/skynetjoellc