Back to all cases

TouchLink

A hub that receives and delivers leads: it collects them from ad sources, normalises them and hands them off to CRM and analytics. More than 50 active customers.

Introduction:

The client builds CRM software for property developers. Their customers ran a separate service on the side: it pulled leads out of Facebook ad accounts and dropped them into the CRM. It was run by an outside contractor — a studio with a name on the market.

At some point the contractor let everyone down. Fifty companies were left without a channel between their advertising and their sales teams. The ad money keeps going out, the leads keep coming in, and none of them reach the sales managers.

When the question came up of what to do with those customers, I took three days for a first version.

What follows is the interface of that very first version, which is still taking leads today. Company names, personal names and phone numbers are hidden in the screenshots: company and lead data is not shown.

Overview screen of the first TouchLink version: lead counters and a table of connected companies
Overview: lead counters and a table of connected companies — one row each.

How it went:

The first version looks nothing like what runs now. I took the simplest methods, the ones proven by time — essentially a hand-tuned algorithm. Plain PHP, no build step, a customer is created as a directory on disk. Make a folder — get a webhook address.

Sounds crude. But a customer gets connected in a minute, not through a database migration.

Customer connection card in the first TouchLink version: name, slug, source, CRM installation address and action
The connection card: source, choice of CRM installation and action. The slug is the name of the directory on disk, and it can’t be changed once created.
TouchLink monitoring screen: queue progress, state counters and three background jobs with logs
Monitoring: the queue and three background jobs with logs — queue processing, retries and form sync.
Setting up UTM parameters, tags and the feed of data into end-to-end analytics
Campaign tagging: UTM parameters, tags and the feed of data into end-to-end analytics.

For the first few months the project looked like a closed admin panel for the rollout and support team. The customer had nothing but notifications in Telegram. That was the plan: first the leads simply had to move.

Along the way we closed gaps the previous contractor hadn’t touched for years. End-to-end campaign tagging, tags, sending rejection reasons back into advertising, choosing the CRM installation by country — the company has five of them.

The fire was put out. After that the service spent several months turning into what is running in production today — a simple engine pushed to the limit.

TouchLink dashboard in dark theme: today's lead counters, top sources and a list of recent leads
The dashboard: leads for the period, duplicates, errors and top sources.

A lead in any shape. Everyone’s ad forms are different: the fields are named however the marketer decided to name them. If the name and the phone number sit in fields that make sense, the lead goes straight through. If not, a language model takes it apart.

The model runs on my own server. That is a matter of principle: a lead holds the name and phone number of a real person, and data like that must not leave the environment. There are no cloud services anywhere in this chain — from the ad account to the CRM, the lead is never shown to a third party.

If the model doesn’t manage either, there is a third stage — parsing the raw payload with regular expressions.

TouchLink lead log: a table of leads with source, form, recipient and delivery status
The lead log: source, form, recipient and delivery status for every lead.
TouchLink lead delivery monitor in light theme: filters by period, source, destination and status
The delivery monitor: filters by period, source, destination and status.

Delivery with nothing lost. A webhook takes the lead and puts it in a queue, and cron processes the queue once a minute. If the CRM doesn’t answer, the lead isn’t lost: retries go at a growing interval — 15 minutes, half an hour, an hour, three hours, eight. There is a separate backfill as well: if the outage happened before the fix, the missed leads arrive after the fact.

Duplicates were what I feared most. Calling a person twice is worse than not calling at all. Every lead carries a delivery mark — a retry sees it and passes by.

TouchLink dashboard in light theme: today's leads, duplicates, errors and leads in processing
The same dashboard in light theme and in Russian.

data as of 2 September 2026

57

connected companies

77

ad pages

3,423 lead forms

~1,950

leads a day

around 78,000 a month

100%

delivery with nothing lost

delivered successfully over the last week of observation


Result:

The service is in its seventh month. Fifty-seven companies, seventy-seven ad pages, three and a half thousand forms.

More than 459,000 leads have gone through it. Around two thousand a day. Over the last week of observation not a single one was lost.

The second version is live now: automatic billing, client portals with their own settings, centralised management.


Stack:

Backend — Laravel, PostgreSQL, queues on Redis. Frontend — Vue and TypeScript. Deployed with Docker.