Факапы автоматизации: 7 причин, почему заявки дублируются и как это отлавливать - Блог Папы Карло

Опубликовано: 5 месяцев назад
Просмотров: 88

Привет. Я в своей мастерской такие истории ловил уже не раз: вроде собрал аккуратную автоматизацию, кнопки щёлкают, webhook летит, CRM бодро принимает лиды — а потом смотришь, и один человек внезапно превратился в два, а то и в три “новых” обращения. Обычно причина не в том, что “бот сошёл с ума”, а в том, что одно и то же событие зашло в систему повторно, а защита от дублей стоит не там, где надо.

Если упростить до сути: заявки дублируются из-за повторной отправки формы, повторной доставки webhook, кривой нормализации телефона и почты, гонок между сценариями, нескольких источников одного лида и повторных прогонов workflow. Ниже покажу, где именно это рождается, как я это вычисляю по логам, и какой чек-лист ставлю в боевую схему, чтобы дубли лидов не расползались по CRM.

Сначала дам быстрый маршрут. Разберём 7 причин, потом пройдёмся по диагностике, а в конце я оставлю рабочую схему защиты: dedupe key, идемпотентность webhook, атомарная запись и мониторинг повторов. Плюс добавлю маленькую таблицу, чтобы можно было быстро понять, где копать в первую очередь, если у вас дублируются заявки в CRM.

Содержание

Рабочий стол с монитором, где видны повторяющиеся карточки лидов и схема автоматизации

Как понять, что у вас не “глюк в CRM”, а реальный поток дублей

Тут народ часто начинает ругаться на CRM, таблицу, менеджера или интегратора, а надо на минуту остыть и посмотреть на картину шире. Если дубль прилетает в течение пары секунд, очень часто это повторная отправка формы или повторная доставка события. Если дубли появляются через минуту, пять или десять, тут уже пахнет retry, ручным rerun или вторым каналом передачи.

Я обычно смотрю на четыре вещи:

  • время создания записей — разница в 1–10 секунд уже много о чём говорит;
  • совпадение телефона, email или внешнего ID — если поля те же, дубль почти наверняка технический;
  • путь заявки — форма, бот, реклама, менеджер, импорт, API;
  • историю исполнения — не было ли retry, повторной доставки webhook или ручного повтора сценария.

Когда вижу две записи с одинаковым номером, но в одной телефон приходит как +7 999 123-45-67, а в другой как 89991234567, я уже понимаю: проблема не в “странной CRM”, а в том, что нормализация данных стоит после создания лида, а не до него. Это классика. Если вам интересна вся цепочка движения обращений, у меня рядом есть материал про связку заявки, расписания и напоминаний — там хорошо видно, как один кривой этап тянет хвост проблем дальше по всей системе.

Где именно рождаются дубли: 7 причин

Теперь к мясу. Ниже семь причин, которые я чаще всего встречаю в автоматизации малого бизнеса, сервисов и мастерских.

1. Форму отправляют повторно, а кнопка не блокирует вторую попытку

Человек жмёт “Отправить”, страница подвисает на секунду, и палец автоматом тыкает ещё раз. Иногда пользователь обновляет страницу, иногда браузер или фронт отправляет форму повторно после странного поведения скрипта. В итоге вы получаете две одинаковые заявки почти одновременно.

Как это выглядит: два лида с разницей 1–3 секунды, одинаковый payload, один и тот же источник, одинаковый user agent. Лечится это блокировкой кнопки после первого клика, серверной защитой от повторной отправки формы и коротким окном дедупликации по fingerprint события.

2. Webhook пришёл повторно после таймаута или потери ответа

Вот тут уже начинается взрослая автоматика. Многие сервисы доставляют события по модели at-least-once, то есть одно и то же событие может прилететь повторно, если отправитель не увидел подтверждение вовремя. Снаружи вам кажется, что всё прошло нормально, а провайдер думает: “ответ мутный, давай-ка ещё разок”. И вот у вас один webhook пришёл два раза, а лид создался дважды.

Это частая причина, почему заявка создается дважды в связках с формами, ботами, оплатами и внешними API. Особенно весело, когда сценарий сначала создаёт запись, а уже потом долго считает что-то, пишет в таблицу и отвечает источнику. В таком раскладе повторная доставка почти напрашивается.

Я в таких местах стараюсь как можно раньше вернуть корректный ответ источнику, а тяжёлую логику увести дальше по пайплайну. И обязательно завожу dedupe key на базе внешнего event_id или своего устойчивого ключа.

3. Нет единого ключа для дедупликации

Это вообще самый жирный косяк. Народ пытается ловить дубли по имени, телефону, почте, тексту сообщения, ещё и в разном порядке. А потом удивляется, почему дедупликация заявок работает через раз. Пока у вас нет одного понятного ключа, система будет гадать, а не проверять.

Нормальный вариант — брать внешний ID события, если источник его даёт. Если не даёт, собираем свой ключ: нормализованный телефон или email, плюс канал, плюс тип формы, плюс короткое временное окно. Для части кейсов хватает одной проверки по номеру, и тут вам пригодится опыт из статьи про дедупликацию по телефону в Notion, но в боевых схемах я люблю смотреть шире, чем на одно поле.

4. Телефон, почта и имя приходят в разном виде, и CRM считает их разными

Один и тот же номер может прийти как +79991234567, 8 (999) 123-45-67, 9991234567. Почта то с заглавными буквами, то с пробелом на краю, то вообще с дополнительным алиасом. Если вы сравниваете сырые данные, а не нормализованные, система честно скажет: “похоже на разных людей”.

Поэтому нормализация должна жить до этапа поиска и создания лида. Сначала чистим пробелы, дефисы, скобки, регистр, приводим телефон к одному формату, а уже потом делаем поиск дубля. Если наоборот — ловите цирк. Это одна из главных причин, почему “как убрать дубли лидов” превращается в длинный проект вместо одного аккуратного фикса.

5. Проверка дубля и создание записи идут двумя отдельными шагами

Сценарий делает так: сначала “найти контакт”, потом “если не найден — создать”. На бумаге выглядит логично. На нагрузке ломается. Два параллельных запуска одновременно проверили, что записи нет, и оба тут же создали новую. Всё, приехали: гонка состояний, она же race condition.

Признак: два почти одновременных исполнения, оба видят пустой результат поиска, а потом оба создают запись. Тут спасает атомарность: unique index в базе, upsert, lock в Redis, транзакция или отдельная таблица idempotency keys. В no-code и low-code мире этот момент часто упускают, потому что визуально схема красивая, а под капотом два конкурентных процесса.

6. Один и тот же лид летит из нескольких каналов сразу

Вот это мой любимый “невидимый” фейл. Клиент оставил заявку в форме, потом написал в бот, а ещё реклама или коллтрекинг протолкнули его как отдельный лид. Снаружи вы видите три источника, внутри — одного и того же человека. Если не сводить каналы через единый слой матчинга, CRM начинает плодить клоны.

Особенно часто такое случается там, где уже настроена обработка заявок из Telegram в Google Sheets, а рядом живёт форма сайта и ручной ввод менеджера. Каждый канал уверен, что именно он принёс “нового” клиента. Тут помогает единая точка приёма лидов и обязательная сверка по нормализованным данным до записи в конечную систему.

7. Retry, rerun и ручные перезапуски плодят повторные записи

Иногда дубль делает не клиент и не источник, а мы сами. В n8n, Make и похожих системах включили retry на запросе, потом руками перезапустили execution, потом ещё error workflow добросил сигнал в тот же канал — и вот уже воронка получила клон. На вид всё полезно: надёжность, восстановление, контроль ошибок. На деле, если side effect не идемпотентный, то повтор превращается в лишнюю запись.

Поэтому я всегда отдельно проверяю историю ошибок и повторных прогонов. Для n8n вообще полезно заранее продумать контроль ошибок и retries, а если в цепочке есть ИИ-ответы, то ставить ещё и контрольную точку перед отправкой клиенту. Это сильно снижает шанс, что автоматика повторит действие просто потому, что кто-то решил “ну давай ещё раз запустим”.

Как я это отлавливаю на практике

Когда ко мне прилетает задача “почему дублируются заявки”, я не лезу сразу перекраивать весь сценарий. Я сначала раскладываю трассу события. Вот мой рабочий порядок:

  1. Фиксирую путь события. Откуда пришло: форма, бот, CRM, реклама, таблица, ручной ввод.
  2. Вешаю correlation_id. Один ID должен пройти через все шаги — от входа до создания лида.
  3. Сохраняю сырой и нормализованный payload. Иначе вы не поймёте, на каком этапе поля “переобулись”.
  4. Смотрю тайминг. Секунды между дублями часто прямо указывают на причину.
  5. Проверяю историю запусков. Был ли retry, error workflow, ручной rerun, повторный webhook.
  6. Сравниваю ключи. Есть ли общий dedupe key у обеих записей, и если нет — почему его не собрали.

Ещё я люблю маленькую табличку-диагностику. Она экономит кучу времени, когда надо быстро понять направление удара.

Симптом На что похоже Что смотреть первым
Два лида за 1–3 секунды Двойная отправка формы Логи фронта, блокировку кнопки, request ID
Те же данные через 10–60 секунд Повторная доставка webhook или retry HTTP-ответ источнику, event_id, execution history
Одинаковый клиент, разный формат телефона Нормализация после создания записи Преобразование номера и email до поиска дубля
Два запуска создали запись одновременно Race condition Поиск/создание, unique index, upsert
Лид пришёл из формы и бота Несколько входных каналов Единый слой матчинга и маршрутизации

Если тема вам сейчас особенно близка, ещё советую заглянуть в материал про то, как не тонуть в потоке заявок. Он хорошо дополняет эту статью с точки зрения общей архитектуры.

Схема защиты, которая держит повторные события

А теперь самое полезное. Если бы мне надо было собрать защиту от дублей с нуля, я бы делал так:

  1. Принимаю событие и сразу присваиваю ему устойчивый ключ. Внешний event_id или свой dedupe key.
  2. Нормализую данные до любых сравнений. Телефон, email, имя, форма, канал.
  3. Проверяю ключ атомарно. Не “нашёл → потом создал”, а “вставил или получил конфликт”.
  4. Только после этого делаю side effect. Создание лида, сделки, задачи, сообщения.
  5. Логирую исход. Новый лид, обновление существующего, дубль отброшен, ошибка, retry.
  6. Развожу быстрый ответ и тяжёлую обработку. Источник получает подтверждение раньше, чем вы разворачиваете весь оркестр узлов.

Главная мысль: проверка дубля должна быть ближе к входу, чем любое действие, которое меняет данные. Если сначала создали запись, а потом решили “сейчас проверим, не дубль ли это”, то вы уже опоздали.

В n8n и похожих инструментах я ещё люблю вести отдельный журнал дублей: timestamp, source, dedupe key, причина отсечения, execution ID. Потом этот лог отлично показывает, где автоматика начала чудить. А если хочется посмотреть на тему чуть шире, то мои заметки о фейлах автоматизации тоже пригодятся: что дают реальные разборы неудачных сценариев.

Чек-лист перед запуском автоматизации

  • Есть ли у каждого входного события свой dedupe key?
  • Нормализуются ли телефон и email до поиска существующей записи?
  • Блокируется ли повторный клик по кнопке отправки?
  • Возвращает ли webhook-обработчик быстрый и понятный ответ источнику?
  • Идёт ли запись в CRM через upsert, unique constraint или другой атомарный механизм?
  • Разделены ли создание лида и уведомления, чтобы повтор алерта не плодил запись заново?
  • Видно ли в логах retry, rerun, execution ID и источник события?
  • Есть ли отдельный сценарий оповещения об ошибках, а не повтор боевого действия вслепую?

Что в итоге делать прямо сейчас

Если у вас уже летят дубли, не надо сначала переписывать весь пайплайн. Возьмите последние 20 повторных заявок и руками проверьте три вещи: разницу по времени, совпадение нормализованных контактов и историю запусков. Очень часто корень всплывает уже на этом этапе.

Дальше ставьте минимальный набор защиты: dedupe key на входе, нормализацию до поиска, атомарную проверку, отдельный лог дублей и аккуратную схему retry. После этого даже если webhook дернётся повторно или кто-то заново прожмёт сценарий, система не начнёт лепить лишние лиды, как конвейер на максималках.

Короче, когда в автоматизации плодятся клоны заявок, проблема почти всегда не мистическая, а инженерная. Надо не гадать, а смотреть, где именно событие второй раз получает право создать запись. Находите эту точку — и половина боли в отделе продаж заканчивается в тот же день.

Метки: , , , , , , , , ,