I take a manual business process and make software do it.
End to end, in production, for businesses that are not
technical. A roofer who cannot answer his phone on a roof. A sales team
exporting CSVs to work out how last month went. A list of 2,277 companies
that has to become the forty worth calling.
I have built the system and sold the thing the
system does, so I can sit with a non-technical owner, work out what they
actually do all day, and ship the integration that replaces it.
A voice agent that answers inbound calls for UK roofing
contractors, qualifies the caller, books the job into a live calendar, and
sends the owner a written summary after the call. Sold at £395/month.
The problem. A roofing contractor is thirty feet up with
his hands full. The phone rings, and a missed call is a lost job — not a lost
lead, a lost job, worth more than the software costs in a year. Voicemail does
not fix it, because nobody leaves one.
Decisions worth naming
Agent prompts and per-client knowledge bases are versioned in a
separate repo from the site, so a client's script changes without
touching deployed code or waiting on a release.
The demo is a real phone call, not a video. You give it
a number and it rings you. Every objection a sceptical owner has is about
whether it sounds like a person, and no recording settles that.
A US variant of the site is built and parked behind a
Next.js private folder — never routed, never served, one git mv
from live. Market tests should be cheap to start, not cheap to abandon.
An MCP server over the CloudTalk REST API, so call history,
agent activity and conversation-intelligence data can be queried in plain
language from a Claude chat — plus a standalone CLI that runs from cron.
The problem. CloudTalk ships no official MCP server, and
the third-party wrappers expose only contact and activity CRUD — not call
history, recordings, or the AI layer. Answering "how did outbound go last
month" meant exporting CSVs by hand, every month.
Decisions worth naming
The vendor's own OpenAPI document is wrong about its base
URLs. CloudTalk is served from four different hosts, and the
correct ones appear only in per-operation overrides. The client routes by
path. I vendored the spec into the repo so the mapping can be checked rather
than trusted.
The rate limit is 60 requests a minute, so the API is not the
query layer. Sync a date range into local SQLite once; every
analytical question over that range is then answered offline and instantly.
Re-syncing replaces rows rather than duplicating them.
The obvious field is a trap, and I have the numbers.
CloudTalk stamps answered_at when ringing stops, so it is
populated on calls nobody picked up — use it and you report a 100% connect
rate. talking_time > 0 reproduces CloudTalk's own
answered/missed split exactly: 48/48 and 13/13, zero misclassified either
way.
The tool refuses to tell you what you want to hear.
Every rate carries a 95% Wilson confidence interval with its denominator
named, and a period comparison will not return a verdict at all unless the
baseline has 30+ dials across 3+ days. Eleven setup calls at a 91%
conversation rate will test as "significant" against two real dialling days
and mean nothing, so it returns null and says why.
UK roofer lead pipeline
Node · 6 stages
Node.js · Apify (Google Maps) · Companies House API · Pipedrive API
Scrapes UK roofing contractors from Google Maps, enriches them
against Companies House, scores them for missed-call propensity, and pushes
the ones worth calling into the Pipedrive Leads Inbox with a call brief
attached. 2,277 businesses in, 1,577 through the filters, ranked.
The problem. The product is sold on "a missed call is a
lost job", so the target is not a roofer — it is a roofer who
demonstrably cannot answer his phone. No list you can buy encodes
that, so the ranking had to be built rather than bought.
Decisions worth naming
Every signal in the scoring model has a stated reason, and the
negative ones matter most. A mobile 07 number is +35, because the
owner is the receptionist. An 0800 freephone is −25
— they already paid to have calls answered, so they are the wrong prospect,
not a weaker one.
Some things bypass scoring entirely. Dissolved
companies and premium-rate lines are hard-blocked and never reach the CRM
regardless of how well they score. TPS and do-not-call screening run as
their own stages.
Six numbered stages, each re-runnable on its own, each
writing JSON to disk — so fixing the cheap scoring stage never means paying
to run the expensive scraping stage again. DRY_RUN=1 prices a
run before it spends anything.
The pipeline deliberately stops short of the CRM.npm run all produces a review CSV and halts. A human reads it
before anything irreversible reaches a real prospect.
Funnel, calculator and revenue plumbing
TypeScript · server-side events
Next.js App Router · TypeScript · Vercel · Supabase · Pipedrive · Meta Pixel + Conversions API
The marketing site behind all of it: an interactive
revenue-leak calculator, a gated assessment funnel writing through
/api/lead to Supabase and on into Pipedrive with UTM and
fbclid carried through, and instrumentation designed so the
numbers can be trusted.
The problem. Ad platforms optimise on the events you send
them. A real share of conversions never reach a browser pixel because of ad
blockers, so the platform learns from a biased sample and the funnel cannot be
diagnosed honestly.
Decisions worth naming
Three destinations, each with a different job — Vercel
Analytics (cookieless, every visitor) is the measurement of record; the
browser pixel drives ad optimisation; the server-side Conversions
API fires on every gate submission including ad-blocked ones,
recovering the leads the pixel misses.
A written rule about which number to trust when they
disagree. Read funnels from Vercel, not Meta. Meta's counts will
always sit lower, and that gap is ad blocking, not a bug — a distinction
worth documenting once instead of relitigating monthly.
The standard-event list is kept deliberately short
— only standard events can be chosen as an optimisation goal in Ads Manager,
so a long list makes the important one meaningless. Nine further custom
events exist purely to filter on.
What I'm good at
Voice and AI agents
Retell and Twilio in production on a live line. Prompt and knowledge-base
design, post-call summarisation, and the handoffs that only show up once real
callers are on it.
Integration against hostile APIs
CloudTalk, Companies House, Pipedrive, Apify, Meta Conversions API,
cal.com. Rate limits, wrong documentation, fields that lie, and local caching
layers when the API cannot be the query layer.
MCP and tooling
Wrote an MCP server from scratch against an API that had none — 20 tools,
four hosts, a SQLite mirror, and a smoke test that reports which capabilities
an account actually exposes.
Revenue plumbing
CRM engineering, lead scoring, deduplication and suppression, server-side
conversion tracking, funnel instrumentation, and compliance screening on
outbound.
Shipping to non-technical users
Everything above runs for people who will not read a log. Human review
gates before irreversible steps, dry runs before spend, and verification
commands that say exactly what is wrong.
Talking to the customer
Years of cold outbound and closing before any of this. I can run the
discovery call, hear the actual process under the stated one, and scope the
build from it.
Background
Founder / Engineer, Musanhi — June 2025 to now. Built,
sold and run the product end to end. Selling it myself is where the product
decisions came from: the callable demo and the post-call summary both exist
because of objections raised on real calls with sceptical owners.
Client acquisition, Closing Consulting — May to August
2026. Outbound for a consultancy that builds and runs the sales operation for
coaching and agency businesses doing $30K–$1M a month. Their systems layer —
CRM configuration, KPI tracking, end-of-day reporting — is the same job the
CloudTalk server above does.
Freelance web development — 2024 to 2025. Built and sold
sites to six clients sourced entirely through cold outbound, running the
outreach, the close and the delivery.
The commercial years aren't a detour from the engineering. They're why the
software gets built around what a business actually loses money on, rather than
around what's interesting to build.