Build report 003 / Internal platform
From a spreadsheet to a lead engine.
How we replaced a Google Sheets lead tracker with a self-hosted five-service stack that files every Meta lead in under five seconds, follows up on its own every morning, and teaches the ad algorithm which leads actually become customers.
The problem
Before this system, leads lived in a Google Sheets tracker. Someone checked Meta Ads Manager, copied each lead's details into the sheet by hand, and updated a status column when they remembered to. Twelve different ad-hoc statuses had accumulated ("No Answer", "Info Sent", "Later", "Nurture") with no shared definition of any of them.
Three things were quietly expensive. Speed: a lead that arrives at 21:00 sat untouched until someone opened the sheet. Follow-up: there was no system. If a lead went quiet, nothing noticed. And attribution: Meta never learned which leads became paying customers, so the algorithm kept optimising for cheap form-fills instead of revenue.
A spreadsheet is a fine ledger. It's a terrible engine.
01 / The workflows
Three jobs, running on their own.
Ingestion: on every lead
A Meta Lead Ads webhook, a website form post, or an inbound WhatsApp message arrives. The payload is normalised: name, email, phone, budget, source.
A contact and a deal exist in the CRM in under five seconds, stage NEW, with the budget parsed into a deal value. No typing, no lag, no lost leads.
Nurture: daily at 09:00
The system queries every open deal and finds the ones that have gone quiet: no contact in more than three days.
Each stale lead gets a WhatsApp follow-up written for its pipeline stage (a new enquiry gets a different message than a sent proposal) and the contact date updates so nobody gets messaged twice.
Attribution: hourly
Deals that reach the CUSTOMER stage are collected, and the contact details are SHA256-hashed. No readable personal data leaves the system.
Each closed deal is reported to the Meta Conversions API as an offline Purchase event with its real value. The ad account stops optimising for form-fills and starts optimising for revenue.
02 / What we built
Owned infrastructure, not another subscription.
Five services, one compose file
Twenty CRM, n8n, Evolution API, PostgreSQL 16 and Redis, containerised with Docker, fronted by Traefik with automatic Let's Encrypt certificates.
The migration script
A Python script read the legacy sheet through the Google Sheets API and mapped its twelve inconsistent statuses onto a clean five-stage pipeline: NEW → SCREENING → MEETING → PROPOSAL → CUSTOMER.
Custom CRM fields
Every deal carries leadSource, budgetRange, lastContactDate and capiSynced, the fields the automations need to route follow-ups and prevent duplicate conversion reports.
Lead-form hardening
The Meta lead form was switched to Higher Intent optimisation with SMS verification and budget-based disqualification, so fewer junk leads enter the pipeline at all.
Backups and runbook
Daily automated database backups with 30-day retention, plus written setup and operations documentation. The system survives a bad day.
Roughly R2,000/month, all-in
The entire stack runs on one VPS. Open-source software, no per-seat licences: the cost of a SaaS CRM for one user, for the whole system.
Why it matters for your ads
Most businesses running Meta ads optimise on form-fills, because that's all the pixel can see. The expensive insight, which leads became paying customers, stays trapped in a spreadsheet or an invoice book.
Closing that loop is the entire point of this system. When a deal closes, Meta hears about it within the hour, tied to the original ad click. Over time the algorithm builds its audience around people who resemble your customers, not your tyre-kickers. That's the difference between buying traffic and buying revenue.
And when you message us through this site, this is the system that catches it. We run our own pipeline on the same engineering we quote you for.
Want operations that run themselves?
This is the Web Platform tier in practice: self-hosted systems, automation, and integrations on infrastructure you own. Send one message and tell us what your team still does by hand.