Lead Capture
Ответы на исходящие письма превращаются в размеченные лиды и назначенные встречи — без человека, читающего каждый ящик.Outbound email replies become labelled leads and booked meetings, without a person reading every inbox.
Leadhype — B2B-агентство лидогенерации в США, которое ведёт исходящие кампании с примерно 5 000 отправляющих ящиков. Ответы возвращались в эти же ящики, и часть положительных терялась. Я написал спецификацию, собрал сервис с AI-ассистированной разработкой и веду его в проде.Leadhype is a U.S. B2B lead-generation agency running outbound campaigns from about 5,000 sending mailboxes. Replies came back across those mailboxes, and some positive ones were lost. I wrote the spec, built the service with AI-assisted development, and run it in production.
ПроблемаThe problem
Исходящие кампании отправляются с тысяч ящиков через Smartlead. Ответы приходят туда же: часть — автоответы и отказы, часть — «да, давайте созвонимся». Их нужно было найти, разметить, передать команде и убедиться, что встреча действительно назначена.Outbound campaigns go out from thousands of mailboxes through Smartlead. Replies come back the same way: some are auto-replies and opt-outs, some say “yes, let's talk”. Those had to be found, labelled, handed to the team, and checked against whether a meeting was actually booked.
Аудит пайплайна показал 36 положительных ответов, на которые никто не ответил с августа.An audit of the pipeline found 36 positive replies nobody had answered since August.
Единого источника истины не было: ответы жили в почтовых ящиках, бронирования — в Calendly, а статус лида — в головах людей. Любое изменение процесса требовало ручной сверки.There was no single source of truth: replies lived in inboxes, bookings in Calendly, lead status in people's heads. Any process change meant manual reconciliation.
до v0.1 в продеfrom spec
to v0.1 in production
найдено аудитомunanswered leads
found by the audit
за пайплайномsending mailboxes
behind the pipeline
Smartlead · LLM · Slack · Calendlyexternal integrations
Smartlead · LLM · Slack · Calendly
Как это работаетHow it works
Шесть шагов от письма до встречи. Каждый шаг идемпотентен: событие можно безопасно обработать дважды.Six steps from an email to a meeting. Every step is idempotent: an event can safely be processed twice.
Smartlead шлёт событие ответа; задача в trigger.dev принимает его и записывает в Supabase.Smartlead sends a reply event; a trigger.dev task receives it and records it in Supabase.
Повторные доставки и ретраи схлопываются: один ответ — одна запись лида.Repeat deliveries and retries are collapsed: one reply, one lead record.
Модель получает текст ответа и небольшой фиксированный набор меток намерения. Узкий набор держит точность и делает ошибки заметными.The model gets the reply text and a small, fixed set of intent labels. The narrow set keeps accuracy up and makes mistakes visible.
Только реальные лиды уходят в рабочий канал команды вместе с текстом ответа и его контекстом.Only real leads go to the team's working channel, together with the reply and its context.
Бронирования сопоставляются с лидами, чтобы «интерес» превращался в «встреча назначена», а не оставался в Slack навсегда.Bookings are matched to leads so that “interested” becomes “meeting booked” instead of living in Slack forever.
Если webhook не пришёл, поллер каждые 30 минут подхватывает пропущенное. Heartbeat и алерты: квоты API, падения задач и отказавшие bounce-webhooks дают сигнал, а не тишину.If a webhook never arrives, a poller running every 30 minutes picks up what was missed. A heartbeat plus alerts: API quotas, failed runs and broken bounce webhooks raise a signal instead of silence.
Решения, которые имели значениеDecisions that mattered
Спецификация до кодаSpec before code
Одна страница: источники событий, модель данных, набор меток, критерии приёмки по существующему ручному процессу. Утверждена CEO до начала сборки — спорили о тексте, а не о готовом коде.One page: event sources, data model, label set, acceptance criteria against the existing manual process. Approved by the CEO before the build started: we argued about the text, not about finished code.
Webhook плюс поллинг, а не «или»Webhook plus polling, not either
Webhooks быстрые, но теряются; поллинг надёжный, но медленный и ест квоту. Вместе они дают скорость и полноту, а дедупликация делает их безопасными.Webhooks are fast but get lost; polling is reliable but slow and burns quota. Together they give speed and completeness, and deduplication makes them safe.
Узкий набор метокA narrow label set
Фиксированный набор меток вместо свободного текста. Меньше свободы для модели — меньше ошибок и проще проверить выборку руками.A fixed set of labels instead of free text. Less freedom for the model means fewer mistakes and an easy manual spot-check.
Исторический backfillHistorical backfill
Сервис прогнали по накопленным ответам до запуска. Так нашлись 36 неотвеченных лидов и появилась проверка качества разметки на реальных данных.The service was run over the accumulated replies before go-live. That surfaced the 36 unanswered leads and gave a quality check of the labelling on real data.
Приёмка по старому процессуAcceptance against the old process
Результаты сервиса сверялись с тем, что давал ручной процесс; расхождения разбирались по одному. Только после этого ручной шаг убрали.The service's output was checked against what the manual process had produced; differences were reviewed one by one. Only then was the manual step removed.
СтекStack
Что ломалось в продеWhat broke in production
Та часть, о которой спрашивают на интервью.The part people ask about in interviews.
Конфликт маршрутизации webhooksWebhook routing conflict
События приходили не тому обработчику. Развели маршруты и добавили проверку источника события.Events were reaching the wrong handler. Separated the routes and added validation of the event source.
Квота API кончалась молчаAPI quota ran out silently
Поллинг останавливался без единого алерта. Добавили оповещение задолго до лимита.Polling stopped without a single alert. Added an alert well before the limit is reached.
Отказавшие bounce-webhooksFailing bounce webhooks
Отскоки не доходили, и всё, что от них зависит, молчало. Починили доставку и поставили её под тот же мониторинг.Bounces were not arriving, so everything downstream of them stayed silent. Fixed delivery and put it under the same monitoring.
Моя роль и как это собраноMy role, and how it was built
Я отвечал за сервис целиком: проблема, спецификация, модель данных, интеграции, запуск и эксплуатация. Код в основном написан Claude Code по моей спецификации; я проверял, тестировал на реальных ответах, разворачивал и держу сервис в проде.I owned the service end to end: problem, spec, data model, integrations, go-live and operations. Most of the code was written by Claude Code from my spec; I reviewed it, tested it on real replies, deployed it, and keep the service running in production.
Это сознательный выбор, а не компромисс: за неделю в одиночку такой сервис иначе не собрать. Моя ценность здесь — в том, что система правильно спроектирована, честно проверена и переживает отказы, а не в том, сколько строк я набрал сам.This is a deliberate choice, not a compromise: one person cannot build this in a week any other way. My value here is that the system is designed right, tested honestly and survives failures, not how many lines I typed myself.
На этой странице нет названий клиентов и данных кампаний.No client names or campaign data appear on this page.