Business Energy Quote Funnel
A six-step lead funnel that works without JavaScript and finds a UK street from a postcode without any paid API.
- Role
- Designer & engineer
- Team
- Solo
- Timeline
- Sep 2026
- Organisation
- Green Compare Ltd
- Code
- Proprietary (employer)
- Reading time
- 2 min
A zero-dependency landing page and Node server for business-energy quotes, with progressive enhancement, ad attribution, spam protection and an offline postcode-to-street index built from open data.
Overview
Business owners comparing energy prices want a quote, not a form marathon. The funnel asks one question per screen — postcode and address, energy type, current supplier, monthly spend, contract end date, contact details — and posts the lead to a tiny server.
The problem
- Address lookup APIs charge per request and need keys.
- Paid traffic must be attributed correctly (UTM parameters and click IDs).
- Forms must still work on old browsers and with JavaScript blocked.
Context & constraints
The page had to be host-agnostic — deployable anywhere — and easy to hand to a CRM later.
Approach
- Progressive enhancement: with JavaScript off, the form posts normally and the server answers with a redirect; with it on, steps animate and validate inline.
- Offline street index: a Python preprocessing script turns the OS Open Names dataset into one gzipped file per postcode district — a grid spatial join with 250 m cells, a 200 m search radius and a 600 m rural fallback. An optional premium API can return full premises.
- Server hygiene: a field allow-list, required-field checks (422), a 50 KB body cap (413), a honeypot that returns a fake success, a path-traversal guard and
nosniff. - Attribution: UTM parameters and click IDs are captured and pushed as a
generate_leadevent. - A thank-you page knows UK calling hours and says "we're calling you now" only when that's true.
Key capabilities
- One question per screen, six steps
- Works without JavaScript
- Offline postcode → street lookup
- Honeypot spam protection
- UTM and click-ID attribution
- Calling-hours aware confirmation
Architecture
Browser
Node server (no dependencies)
Data
Static front end, a ~300-line Node server with no dependencies, and a preprocessed open-data index. Select a component to trace its connections.
Key decisions
ADR-01Preprocess open data instead of paying per lookup
- Context
- Every visitor triggers address lookups; paid APIs make that a running cost.
- Decision
- Build a static, per-district street index once from open data.
- Trade-offs
- Streets, not exact premises (visitors add a building number) — with an optional premium source when it matters.
Quality & delivery
Deployment
One node server.js process; a quick tunnel script shares a local build for review.
Outcome
- Runtime dependencies
- 0
- Node standard library only
- Postcode district files
- 2,864
- Built from OS Open Names
A host-agnostic funnel ready to point at the CRM, with lead capture working end to end.
Lessons
- The cheapest API call is the one you precomputed.
Building something similar?
Tell me what you’re working on — I reply to every serious message.
Related work
Green Compare CRM
A FastAPI modular monolith with a React 19 front end, where Postgres row-level security backs every permission check. I started the repository, then shipped features across finance, telesales, renewals, dashboards and access control.
- TypeScript
- Python
- React
- Ant Design
- TanStack Query
- FastAPI
- +9