Обсудить задачу

Kommo + Google Sheets: двусторонняя синхронизация без Zapier

Двустороннюю синхронизацию между Kommo и Google Sheets можно реализовать без Zapier через прямую интеграцию: webhook Kommo пишет изменения сделок в Sheet в режиме реального времени, а периодический скрипт читает изменения в Sheet и обновляет сделки через Kommo API. Ключевое условие корректной работы - upsert по deal ID, а не слепой append, и защита от гонки записей при одновременных изменениях с обеих сторон.

Нативная интеграция Kommo с Google Sheets и Zapier решают только половину задачи - и именно не ту половину, которая нужна большинству команд. Ниже - архитектура, которая работает в обе стороны.

В проектах Exceltic.dev мы регулярно видим одну и ту же картину: Google Sheets живёт параллельно с CRM как «рабочая таблица» команды. Менеджеры по продажам вносят данные в Sheet, руководитель правит воронку в Kommo, и раз в неделю кто-то тратит два-три часа на ручную сверку. По нашим наблюдениям, около 80% компаний на Kommo имеют хотя бы один активный Sheet, который дублирует часть данных из CRM. Это не патология - это адаптация под реальный рабочий процесс. Задача интеграции - не запретить Sheet, а сделать так, чтобы данные в нём и в CRM не расходились.

В этой статье разбираем архитектуру двусторонней синхронизации без посредников: что конкретно ломается в нативном решении и Zapier, как устроена правильная схема, и какие цифры она даёт на реальном проекте.

Почему нативная интеграция Kommo с Sheets не решает задачу

Нативная интеграция Kommo с Google Sheets - однонаправленная. Она умеет только одно: при появлении новой строки в Sheet создать лид в Kommo. Не сделку - лид. Без кастомных полей, без привязки к существующей сделке, без обновления уже созданных записей.

По данным официальной документации Kommo, нативная интеграция дополнительно ограничена:

  • обработка останавливается на первой пустой строке в таблице
  • изменение заголовков столбцов после настройки ломает интеграцию
  • подключить можно только один лист на одну интеграцию

Главное ограничение: нет синхронизации в обратную сторону. Когда менеджер меняет статус сделки в Kommo, в Sheet ничего не меняется. Когда кто-то правит строку в Sheet, Kommo об этом не знает. Нативная интеграция - это не синхронизация, это разовый импорт.

Почему Zapier здесь не работает

Zapier кажется очевидным решением, но на практике он создаёт три системные проблемы, которые не решаются настройкой.

Дубли строк из-за отсутствия идемпотентности. Zapier при срабатывании триггера leads.update из Kommo делает spreadsheets.values.append - добавляет новую строку. Если одна сделка обновилась пять раз за день, в Sheet появится пять строк с одним deal ID. Zapier не умеет искать существующую строку по ключу и обновлять её - только добавлять. Официальный workaround от Zapier Community - добавить шаг «Find or Create Row» в отдельный лист и Filter-шаг, что утраивает количество API-вызовов и превращает простой зап в конструкцию из 4-5 шагов.

Лимиты Google Sheets API. Google Sheets API v4 имеет жёсткие квоты: 300 запросов в минуту на проект и 60 запросов в минуту на пользователя. Важно: Zapier использует общий Google-проект для всех своих пользователей. При высокой нагрузке на платформе вы получаете 429 Too Many Requests не из-за своего трафика, а из-за соседей по Zapier-инфраструктуре. Это подтверждают треды в Zapier Community - пользователи видят «Quota exceeded for quota group» даже при небольшом объёме своих зап-запросов.

Нет контроля гонки записей (race condition). При двусторонней схеме через Zapier возможна ситуация: менеджер меняет сделку в Kommo -> Zapier пишет в Sheet -> кто-то меняет ту же строку в Sheet -> Zapier читает Sheet -> создаёт новую запись в Kommo. В результате одна сделка размножается или перезаписывается непредсказуемым образом. Zapier не имеет механизма блокировки или версионирования для предотвращения таких конфликтов.

Термин: Идемпотентность - свойство операции, при котором повторный вызов с теми же параметрами даёт тот же результат, что и первый. Правильная запись в Sheet должна быть идемпотентной: если сделка с ID 12345 уже есть в таблице - обновить существующую строку, а не добавить новую.

Что реализует кастомная интеграция

Двусторонняя схема синхронизации

Архитектура состоит из двух независимых каналов.

Канал 1: Kommo -> Sheet (webhook-driven, в реальном времени). Kommo отправляет webhook при каждом событии leads.update и leads.add. Webhook-приёмник (микросервис или serverless-функция) получает payload с полями сделки: id, name, status_id, price, custom_fields, responsible_user_id. Перед записью в Sheet приёмник проверяет наличие строки с этим id через spreadsheets.values.get. Если строка найдена - выполняет spreadsheets.values.batchUpdate с целевым диапазоном. Если не найдена - spreadsheets.values.append с insertDataOption=INSERT_ROWS. Это и есть upsert-логика на уровне Sheets API.

Канал 2: Sheet -> Kommo (polling, каждые N минут). Периодический скрипт читает весь диапазон данных через spreadsheets.values.get. Сравнивает timestamp последнего изменения каждой строки (служебный столбец last_modified_local) с timestamp последней синхронизации. Строки с изменениями после последней синхронизации отправляются в Kommo через PATCH /api/v4/leads/{id} с соответствующими полями. Строки без изменений пропускаются.

Upsert по deal ID

Ключ всей схемы - столбец с deal ID как первичный ключ таблицы. При любой записи в Sheet выполняется поиск строки с нужным ID через spreadsheets.values.get с диапазоном столбца ID (например Sheet1!A:A). Если ID найден - вычисляется номер строки и выполняется точечный batchUpdate по адресу Sheet1!A{row}:{lastCol}{row}. Этот подход исключает дубли независимо от количества обновлений одной сделки.

Защита от race condition

Для предотвращения конфликтов при одновременных изменениях с обеих сторон используется поле sync_source в каждой строке Sheet. При записи из Kommo выставляется значение crm. При записи из Sheet вручную - sheet. Скрипт polling-канала игнорирует строки, где sync_source=crm и timestamp изменения совпадает с последним обновлением из webhook - это означает, что изменение уже пришло из CRM и не требует обратной синхронизации. Дополнительно - глобальный лок: в отдельной ячейке-семафоре (Sheet1!Z1) хранится флаг sync_in_progress. Webhook-приёмник выставляет флаг перед записью и снимает после. Polling-скрипт проверяет флаг перед стартом и откладывает запуск при активном флаге.

Пошаговая архитектура

Шаг 1: Настройка сервисного аккаунта Google

Для работы с Google Sheets API v4 без OAuth-потока под конкретного пользователя используется service account. В Google Cloud Console создаётся сервисный аккаунт, генерируется JSON-ключ. Целевой Google Sheet открывается для редактирования по email сервисного аккаунта (вида [email protected]). После этого интеграция работает автономно без зависимости от токена конкретного сотрудника.

Ключевые Sheets API методы в этой интеграции:

  • spreadsheets.values.get - чтение диапазона для поиска строки по ID
  • spreadsheets.values.append - добавление новой строки (только для новых сделок)
  • spreadsheets.values.batchUpdate - обновление существующих строк по точному диапазону

Шаг 2: Настройка Kommo webhook

В настройках Kommo (раздел «Webhooks») регистрируется endpoint webhook-приёмника. Подписка на события: leads.add, leads.update, leads.status. Kommo передаёт в payload поля сделки включая кастомные поля (custom_fields) с их ID и значениями. Для кастомных полей в webhook используются числовые ID, не названия - маппинг ID на читаемые названия столбцов Sheet хранится в конфиге интеграции.

Шаг 3: Webhook-приёмник

Микросервис на любом языке (Python, Node.js) принимает POST-запрос от Kommo, верифицирует подпись, извлекает данные сделки, выполняет upsert в Sheet. Важно: ответ на webhook должен быть отправлен в течение 10 секунд, иначе Kommo повторит отправку. Для защиты от дублей при повторных отправках - кэш обработанных webhook-ID (Redis или простой файл) на 60 секунд.

Шаг 4: Polling-скрипт для Sheet -> Kommo

Cron-задача с частотой, подходящей под рабочий процесс команды: обычно каждые 5-15 минут. Скрипт читает весь Sheet, фильтрует строки с last_modified_local новее last_sync_timestamp, для каждой такой строки отправляет PATCH /api/v4/leads/{id} в Kommo API. Результат записывается обратно в служебные столбцы: sync_status (success/error), last_synced_at.

Шаг 5: Структура таблицы

Обязательные служебные столбцы, скрытые от пользователей:

  • deal_id - первичный ключ, ID сделки в Kommo
  • last_modified_local - timestamp последнего ручного изменения (обновляется через Apps Script onEdit-триггер)
  • sync_source - crm или sheet
  • sync_status - статус последней синхронизации
  • last_synced_at - timestamp последней успешной синхронизации

Пользовательские столбцы: название сделки, этап, сумма, ответственный, кастомные поля по задаче проекта.

Реальный кейс с цифрами

Компания - B2B-дистрибьютор с командой продаж из 8 человек. Google Sheet использовался как основной рабочий документ для планирования отгрузок: менеджеры вносили даты доставки и контактные данные склада прямо в таблицу. Kommo - для ведения воронки и коммуникации. Каждую пятницу операционный менеджер тратил около 3 часов на ручную сверку данных между Sheet и CRM.

После внедрения кастомной двусторонней синхронизации:

  • Ручная еженедельная сверка: 0 часов (полностью автоматизирована)
  • Задержка синхронизации Sheet -> Kommo: до 10 минут (polling каждые 10 минут)
  • Задержка синхронизации Kommo -> Sheet: секунды (webhook в реальном времени)
  • Расхождений данных между системами за первые 3 месяца эксплуатации: 0
  • Дублей строк в Sheet: 0 (upsert по deal ID)

Объём реализации: около 3 рабочих дней с учётом тестирования на staging-среде и обучения команды работе со служебными столбцами.

Для сравнения: предыдущая попытка настроить синхронизацию через Zapier продержалась две недели, после чего была отключена из-за нарастающего количества дублей и непредсказуемого поведения при одновременных правках.

Для кого подходит эта схема - а кому пора уходить из Sheets совсем

Схема оправдана, если:

  • Google Sheet - реальный рабочий инструмент команды (планирование, логистика, операции), а не просто экспорт для отчётности
  • 1-3 столбца в Sheet редактируются вручную и должны попадать в CRM
  • Команда не готова полностью переходить в Kommo для всех операций
  • Объём активных сделок - до 5000 одновременно (выше - стоит смотреть в сторону полноценного ETL)

Когда Sheet стоит убрать совсем:

  • Если Sheet используется только для отчётности - замените его дашбордом из CRM-данных. Для этого хорошо подходят BI-инструменты с прямым подключением к Kommo или аналитические платформы вроде Prooflytics, которые дают единую картину по сделкам и маркетингу без ручного экспорта.
  • Если в Sheet больше 10 столбцов с бизнес-данными и всё это нужно в CRM - это сигнал, что нужно пересмотреть модель данных в Kommo и настроить кастомные поля, а не поддерживать параллельную таблицу.
  • Если расходы на поддержку синхронизации сопоставимы с ценой перестройки процесса - стоит сделать перестройку.

Если компания переросла Kommo в части аналитики и отчётности, но рабочий процесс в Sheet ещё актуален - интеграция Kommo с n8n даёт более гибкую платформу для оркестрации таких потоков данных с визуальным редактором и self-hosted режимом.

Часто задаваемые вопросы

Можно ли использовать нативную интеграцию Kommo с Google Sheets для двусторонней синхронизации?

Нет. Нативная интеграция Kommo с Google Sheets работает только в одном направлении: новая строка в Sheet создаёт лид в Kommo. Обратная синхронизация - изменения сделок из Kommo в Sheet - нативной интеграцией не поддерживается. Кроме того, нативная интеграция не умеет обновлять существующие записи: каждое срабатывание создаёт новый лид, а не обновляет существующий. Для двусторонней синхронизации с upsert-логикой нужна кастомная интеграция через Kommo webhooks и Google Sheets API v4 напрямую.

Почему Zapier создаёт дубли строк в Google Sheets?

Zapier при срабатывании на событие Kommo использует метод spreadsheets.values.append, который всегда добавляет новую строку - он не проверяет, существует ли уже строка с таким deal ID. Если сделка обновляется несколько раз, каждое обновление добавляет новую строку. Zapier Community предлагает workaround через «Find or Create Row» + Filter, но это не устраняет проблему архитектурно - только снижает частоту дублей ценой утроения числа API-вызовов. Правильное решение - реализовать upsert на уровне кода: сначала найти строку по deal ID через spreadsheets.values.get, затем обновить её через batchUpdate или добавить через append только если строка не найдена.

Насколько быстро синхронизируются данные?

Зависит от направления. Kommo -> Sheet: синхронизация происходит в режиме реального времени через webhook - задержка составляет секунды (время обработки webhook-приёмника). Sheet -> Kommo: синхронизация происходит по расписанию polling-скрипта, обычно каждые 5-15 минут. Это достаточно для большинства операционных сценариев, где данные в Sheet редактируются в рабочем процессе, а не в режиме реального времени. Если нужна немедленная синхронизация Sheet -> Kommo, можно добавить Google Apps Script onEdit-триггер, который при сохранении ячейки сразу вызывает webhook-приёмник.

Как обрабатываются конфликты при одновременном редактировании?

При правильной архитектуре конфликты обрабатываются через приоритет источника и семафор. Если менеджер меняет сделку в Kommo и одновременно кто-то редактирует ту же строку в Sheet, webhook из Kommo записывает изменение в Sheet с меткой sync_source=crm. Polling-скрипт видит эту метку и не отправляет запись обратно в Kommo - иначе получилась бы бесконечная петля синхронизации. Дополнительно используется флаг-семафор в отдельной ячейке, который блокирует polling во время активной записи из webhook. Это не 100% защита от всех edge-cases, но покрывает 99% реальных сценариев использования.

Какой метод Google Sheets API использовать: append или batchUpdate?

Зависит от операции. spreadsheets.values.append - только для добавления новых строк (новые сделки, которых ещё нет в Sheet). spreadsheets.values.batchUpdate - для обновления существующих строк по точному диапазону (например, Sheet1!B5:H5). Никогда не используйте append для обновления существующих записей - это главная причина дублей при интеграции через Zapier и другие no-code инструменты. Правило: всегда сначала искать строку по deal ID через spreadsheets.values.get, и только по результату поиска выбирать метод записи.


Если у вас есть Google Sheet, который живёт параллельно с Kommo и регулярно требует ручной сверки - опишите задачу команде Exceltic.dev. Разберём, какие столбцы нужно синхронизировать в каком направлении, и оценим объём работ по кастомной интеграции.

Ещё статьи

Все →