Интеграция создаёт дубли, когда одно бизнес-событие приходит несколько раз, а обработчик каждый раз выполняет команду «создать». Повтор доставки — нормальная ситуация: webhook может быть отправлен снова, сеть может оборваться после успешной записи, сотрудник может повторить действие. Исправление начинается не с более агрессивного поиска по телефону, а с определения идентичности операции.
Идемпотентность означает простое правило: безопасный повтор одной и той же операции приводит к тому же бизнес-результату. Если заказ уже создан, повтор возвращает его ID, а не создаёт второй.
Повтор доставки и повторный клиент — разные вещи
Клиент может дважды оформить разные заказы с одного телефона. Это две бизнес-операции, хотя контакт один. И наоборот, один заказ может дважды попасть в обработчик из-за повторного webhook. Это одна операция, даже если запросы пришли в разное время.
Поэтому телефон или email полезны для поиска контакта, но не являются надёжным ключом заказа. Для операции нужен собственный внешний идентификатор: номер заказа сайта, ID платежа, ID события или созданный интеграцией UUID.
| Ситуация | Контакт | Сделка или заказ | Ключ решения |
|---|---|---|---|
| Webhook одного заказа пришёл дважды | Один | Одна | Одинаковый ID операции |
| Клиент оформил второй заказ | Один или связанный | Две | Разные ID заказов |
| Одинаковый телефон записан в двух форматах | Нужна нормализация и разбор | Не решается автоматически | Правила качества данных |
| Таймаут после создания сделки | Как в первой попытке | Нужно найти созданную | Сохранённый ключ до вызова API |
Откуда берутся дубли
- Повтор webhook. Отправитель не получил подтверждение и доставил событие снова.
- Повтор пользователя. Двойной клик, повторная отправка формы или возврат сделки на этап снова запускает автоматизацию.
- Таймаут после записи. Получатель успел создать объект, но ответ потерялся; клиент считает попытку неуспешной.
- Параллельная обработка. Два worker одновременно не находят объект и оба создают его.
- Нестабильный поиск. Телефон, email или название компании отличаются пробелом, форматом или устаревшим значением.
- Два источника. Заявка приходит и через штатную форму, и через собственный webhook.
- Ручной обход. Сотрудник создаёт объект вручную, пока автоматическая операция ждёт повторной обработки.
Перед исправлением найдите фактическую причину на одном примере. Простое объединение уже созданных дублей не предотвращает следующие.
Как выбрать идемпотентный ключ
Лучший ключ выдаёт система, где рождается бизнес-операция: order_id магазина, payment_id платёжной системы, shipment_id доставки. Если источник не выдаёт стабильный ID, интеграция создаёт его до начала обработки и возвращает отправителю либо сохраняет рядом с исходной записью.
Ключ должен быть уникален в понятной области. Номер заказа «123» может повториться в двух магазинах, поэтому полным ключом станет `shop-a:order:123`. Не используйте время запроса как единственный ключ: повтор приходит позже и получит другое значение.
Храните связь ключа с объектами обеих систем и состоянием операции. Запись создаётся до внешнего вызова. Тогда параллельная попытка увидит, что операция уже обрабатывается, и не начнёт второе создание.
Безопасный алгоритм обработки
- Проверить подлинность и минимальную полноту входящего запроса.
- Извлечь либо сформировать ключ операции.
- Атомарно зарегистрировать операцию со статусом «получена»; если ключ уже существует, вернуть текущий результат.
- Нормализовать данные и найти связанный контакт или компанию по согласованным правилам.
- Создать или обновить целевой бизнес-объект.
- Сохранить его ID и подтвердить завершение операции.
- При временной ошибке записать попытку и запланировать повтор; при постоянной — отправить на ручной разбор.
Если API самой платформы поддерживает идемпотентные запросы, используйте этот механизм вместе с бизнес-ключом. В истории REST-модуля Битрикс24 поддержка идемпотентных запросов REST API v3 указана в обновлении от 23 июля 2026 года. Это не исправляет старый обработчик автоматически: нужно проверить версию методов и логику приложения. Официальная история версий REST.
Почему поиск контакта не должен быть единственной защитой
Поиск по телефону и email остаётся полезным, но требует нормализации. Телефон приводят к единому формату, email — к согласованному регистру и пробелам. У компании может быть несколько контактов, а один общий номер — у филиала или колл-центра. Автоматическое объединение по слабому совпадению способно смешать данные разных клиентов.
Разделите правила: идентичность операции отвечает за число сделок и заказов; правила master data — за число контактов и компаний. Спорные совпадения лучше отправить в очередь разбора, чем незаметно прикрепить заказ не тому клиенту.
Как исправлять уже накопившиеся дубли
Сначала остановите источник новых дублей или переведите поток в контролируемую очередь. Затем выберите период и соберите группы по внешнему ID, ссылкам на заказ, времени и составу данных. Не объединяйте автоматически только по названию или телефону.
Для каждой группы определите главный объект и связанные сущности: задачи, письма, платежи, документы, товары, комментарии. Согласуйте, что можно перенести, а что останется в истории. После очистки повторите исходный webhook и убедитесь, что новый дубль не появился.
Если внешних ID раньше не было, добавьте их для будущих операций. Исторические данные можно пометить результатом сверки, но нельзя достоверно восстановить то, чего система никогда не сохраняла.
Тесты защиты от дублей
- два одинаковых запроса последовательно;
- два одинаковых запроса одновременно;
- таймаут после успешного создания объекта;
- новый заказ существующего контакта;
- одинаковый телефон в разных форматах;
- изменение данных при повторе того же ID;
- ручное создание объекта до повторной доставки;
- восстановление очереди после перезапуска сервиса.
Для каждого теста сохраните ключ операции, ссылки на объекты и запись журнала. Результат «нет дублей» должен подтверждаться не только визуальным поиском, но и числом созданных объектов и состоянием обработки.
Если существующая связка не хранит ключи и состояние, может потребоваться интеграционный сервис или доработка текущего обработчика. Начните с одного повторяемого примера — он быстрее покажет реальную причину, чем массовая очистка базы.
