← Все статьи

amoCRM в одной правде: двусторонняя синхронизация

amoCRM в одной правде: двусторонняя синхронизация

У тебя сделки живут в amoCRM, а заодно в какой-нибудь внешней системе: своя база, ERP, склад, биллинг или таблица отдела. И обе хотят быть главными. Менеджер поправил сумму в amoCRM, но во внешней системе она старая. Бухгалтер обновил статус оплаты во внешней - в amoCRM об этом никто не знает. В итоге люди вбивают одно и то же по два раза, а потом ещё час спорят, где правильная цифра.

Первое, что приходит в голову - сделать ночной экспорт из amoCRM во внешнюю систему. Но односторонний выгруз проблему не решает: он перезатирает то, что во внешней системе поменяли днём. Нужна синхронизация в обе стороны, причём не раз в сутки, а сразу, как только что-то изменилось. И вот тут начинается самое интересное, потому что у двусторонней синхронизации есть два встроенных капкана, об которые спотыкаются почти все.

Хорошая новость: amoCRM для этого полностью готова. У неё есть нормальный REST API четвёртой версии, доступ по OAuth, и - что важнее всего - вебхуки. Это значит, что amoCRM сама пришлёт тебе уведомление, как только сделку или контакт поменяли. Не надо опрашивать её в цикле и гадать, что изменилось. Остаётся правильно собрать обработчик - и обойти те самые два капкана.

В чём идея

Двусторонняя синхронизация - это не “скопировать данные туда-сюда”. Это поймать изменение с любой стороны и аккуратно перенести его на другую, не сломав при этом ничего. Вебхуки делают первую половину работы: amoCRM шлёт POST на твой адрес при каждой правке сделки или контакта, симметрично ловим изменения и со стороны внешней системы. А вот вторую половину - аккуратность - надо собрать самому. Капканов ровно два.

Капкан первый - зацикливание. Логика простая и оттого коварная. Прилетел вебхук из amoCRM - ты записал изменение во внешнюю систему. Но запись во внешнюю систему рождает её собственное уведомление об изменении - и оно говорит тебе: запиши это в amoCRM. Ты пишешь обратно в amoCRM - а это снова рождает вебхук amoCRM. И понеслось по кругу, пока не упрёшься в лимит запросов или не положишь обе системы. Лечится это памятью: ты запоминаешь отпечаток того, что только что записал сам. Когда прилетает изменение, ты сверяешь: это чужая правка или эхо моей собственной записи? Если эхо - молча гасишь, не пишешь обратно. Цикл разорван.

Капкан второй - конфликт. Бывает, что одну и ту же сделку почти одновременно поменяли с обеих сторон. Кто прав? Нужно правило, иначе системы будут бесконечно переписывать друг друга. Базовое правило - “побеждает свежее” (last-write-wins): сравниваем время последнего изменения с обеих сторон по полю updated_at, и применяем ту версию, что новее. Для критичных полей можно усложнить (“цена всегда из amoCRM”), но по умолчанию хватает времени.

И третье, без чего рассыпается всё остальное - идемпотентность. Сети ненадёжны, и amoCRM может прислать один и тот же вебхук дважды. Поэтому каждое событие обрабатываем строго один раз: ведём журнал уже виденных событий, повтор просто пропускаем. Почему это вообще взлетает на скиле? Потому что современный ИИ-ассистент умеет ходить в REST API, разбирать JSON и держать состояние в файлах. Ему не хватало одного - чёткой инструкции, где прописаны эти три защиты. Скил ровно её и даёт.

Как себе сделать

Не пиши обработчик руками - отдай задачу своему ассистенту (Claude Code, Codex, Cursor, Gemini, любому). Он сам разложит логику по местам: куда положить файл скила, как принять вебхук, как вести карту соответствий id. Перед стартом нужен публично доступный URL для приёма вебхуков - деплой на сервер или туннель (ngrok, cloudflare tunnel); amoCRM не может достучаться до localhost. Тебе нужно только подключить доступ (OAuth-токен amoCRM и настроенные вебхуки на сделки и контакты) и скопировать промпт:

Создай мне скил для двусторонней синхронизации сделок и контактов amoCRM с нашей внешней системой через вебхуки и REST API v4.
Срабатывай на просьбы вроде "синхронизируй amoCRM с нашей системой", "не дублировать ввод сделок", "лови изменения сделок вебхуками".
Доступ к amoCRM - по OAuth 2.0, токен бери из переменной окружения, в текст скила его не вписывай.
Заложи три защиты: идемпотентность (одно событие не обрабатывать дважды), гашение эха (помни отпечаток того, что сам записал, и не пиши его обратно, иначе цикл) и разрешение конфликта last-write-wins по времени изменения updated_at.
Пиши изменения в противоположную систему через REST API v4 amoCRM (эндпоинты сделок и контактов) и веди карту соответствий id amoCRM и id внешней системы.
Логику защиты вынеси в отдельный скрипт, а сам скил оставь короткой инструкцией. Не завязывайся на конкретную ОС или рантайм.

Как понять, что заработало: поменяй сумму у тестовой сделки в amoCRM и посмотри, обновилась ли она во внешней системе - и наоборот. А потом проверь главное: после переноса второй системе тут же прилетает её собственное уведомление, и скил должен его узнать и погасить, а не записать обратно. Если в логах видно одно “synced” и одно “skip - эхо”, без бесконечной ленты - защита от цикла работает, синхронизация живая.

Маленькая честность напоследок: реальные данные двух систем редко ложатся друг в друга один в один - где-то разные справочники статусов, где-то поля называются иначе. Это не про синхронизацию, а про сопоставление полей, и его всё равно описываешь один раз. И ещё: учебный скил помнит один отпечаток на сторону - при пачке быстрых правок старый след стирается, и для продакшна гашение эха хочет очередь отпечатков или TTL-кэш. Зато потом две системы живут в одной правде сами, а менеджеры перестают вбивать одно и то же дважды.

Смотреть полностью

Коротко