amoCRM webhook в n8n: дедупликация событий через Postgres idempotency key ¶
Обновлено: 2026-05-30
Импортируйте JSON в n8n, подключите Postgres credential и замените endpoint бизнес-обработки.
- Проблема: почему amoCRM webhooks приходят повторно и ломают автоматизации
- Архитектура workflow для идемпотентной обработки
- Контракт входных данных (JSON Payload)
- Idempotency key и Postgres unique insert (Code Node + SQL)
- Готовый workflow JSON: скачать и импортировать
- Пошаговая настройка amoCRM webhook, n8n и Postgres
- Тесты перед production и проверка повторных событий
- Production-риски при обработке webhooks amoCRM
- Полезные ссылки и смежные workflow
- Критерии готовности
Проблема: amoCRM webhooks могут приходить повторно, приходить почти одновременно или запускать несколько downstream-действий на одно обновление сделки. Если workflow отправляет уведомление, создаёт задачу или синхронизирует данные без идемпотентности, одно событие превращается в два письма, две задачи и два внешних API-вызова.
Решение: webhook amoCRM в n8n должен сначала построить idempotency_key, затем атомарно записать его в Postgres с unique constraint и только после успешной вставки выполнять бизнес-логику. Повторное событие получает быстрый 200 OK, но не обрабатывается второй раз.
Это production-паттерн для интеграторов: импортируемый workflow JSON, SQL-таблица, Code Node, тест повторного payload и чек-лист запуска.
Проблема: почему amoCRM webhooks приходят повторно и ломают автоматизации ¶
Webhook — это уведомление о событии, а не гарантия “ровно один раз”. CRM может повторить доставку, пользователь может быстро изменить одну и ту же сделку, а ваша логика может ответить медленно. Поэтому фильтр “я уже видел это событие” нельзя держать только в памяти n8n execution.
Особенно опасны действия с побочным эффектом: отправка сообщения клиенту, создание задачи менеджеру, списание бонуса, обновление внешней системы. Повтор такого действия выглядит как баг интеграции, хотя первопричина — отсутствие идемпотентной обработки.
Архитектура workflow для идемпотентной обработки ¶
| Нода | Роль | Что проверить |
|---|---|---|
| Webhook input | Принимает webhook amoCRM | Быстрый ответ, HTTPS, секрет/allowlist при возможности |
| Build idempotency key | Собирает ключ из account/entity/action/time | Ключ стабилен для повтора и отличается для нового изменения |
| Insert idempotency key | Пишет ключ в Postgres | PRIMARY KEY, ON CONFLICT DO NOTHING, отдельная таблица |
| Process once | Выполняет бизнес-действие | Запускается только если insert вернул строку |
| Respond | Отвечает amoCRM | Не раскрывает внутренние ошибки и токены |
Главный принцип: проверка и запись ключа должны быть одним атомарным действием. Нельзя делать “SELECT, потом INSERT” в двух шагах без блокировки — при параллельных событиях оба execution могут пройти проверку.
Контракт входных данных (JSON Payload) ¶
Payload amoCRM зависит от типа сущности и события. Ниже пример обновления сделки. Для контактов и компаний добавьте отдельные ветки в Code Node, но сохраняйте одинаковый принцип ключа.
{
"account": { "id": 301001 },
"leads": {
"update": [
{
"id": 778899,
"updated_at": 1780123456,
"status_id": 12345,
"pipeline_id": 67890
}
]
}
}
Ключ должен включать не только ID сделки, но и действие или timestamp. Иначе разные изменения одной сделки будут считаться одним и тем же событием.
Idempotency key и Postgres unique insert (Code Node + SQL) ¶
Code Node строит ключ, а Postgres гарантирует, что ключ будет принят только один раз. Это надежнее, чем хранить список обработанных событий в static data n8n.
const event = $json.body ?? $json;
const account = event.account?.id ?? event.account_id ?? 'unknown-account';
const leadUpdate = event.leads?.update?.[0];
const leadAdd = event.leads?.add?.[0];
const contactUpdate = event.contacts?.update?.[0];
const entity = leadUpdate ?? leadAdd ?? contactUpdate;
const entityType = leadUpdate || leadAdd ? 'lead' : contactUpdate ? 'contact' : 'unknown';
const action = leadAdd ? 'add' : leadUpdate ? 'update' : contactUpdate ? 'update' : 'unknown';
const entityId = entity?.id ?? event.entity_id;
const updatedAt = entity?.updated_at ?? event.updated_at ?? event.timestamp ?? '';
if (!entityId) {
throw new Error('Cannot build amoCRM webhook idempotency key without entity id');
}
const idempotencyKey = `amocrm:${account}:${entityType}:${entityId}:${action}:${updatedAt}`;
return [{
json: {
idempotency_key: idempotencyKey,
account_id: String(account),
entity_type: entityType,
entity_id: String(entityId),
action,
event_time: String(updatedAt),
raw_event_type: `${entityType}_${action}`
}
}];
SQL для таблицы и атомарной вставки:
CREATE TABLE IF NOT EXISTS idempotency_keys (
key text PRIMARY KEY,
source text NOT NULL,
entity_type text NOT NULL,
entity_id text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO idempotency_keys(key, source, entity_type, entity_id)
VALUES ($1, 'amocrm', $2, $3)
ON CONFLICT DO NOTHING
RETURNING key;
В n8n следующая нода должна проверять результат insert. Если строка не вернулась, это повтор: бизнес-действие пропускается, но webhook отвечает успешно.
Готовый workflow JSON: скачать и импортировать ¶
Полный JSON лежит в архиве сайта и доступен по кнопке. После импорта замените Postgres credential и endpoint Process business action once на ваш внутренний обработчик.
{
"name": "Nodbot - amoCRM webhook deduplication with Postgres",
"nodes": [
{ "name": "Webhook input", "type": "n8n-nodes-base.webhook", "purpose": "Принять webhook amoCRM" },
{ "name": "Build idempotency key", "type": "n8n-nodes-base.code", "purpose": "Собрать стабильный ключ account/entity/action/updated_at" },
{ "name": "Insert idempotency key", "type": "n8n-nodes-base.postgres", "purpose": "Вставить ключ через ON CONFLICT DO NOTHING" },
{ "name": "Process business action once", "type": "n8n-nodes-base.httpRequest", "purpose": "Выполнить бизнес-логику только для нового ключа" },
{ "name": "Respond to Webhook", "type": "n8n-nodes-base.respondToWebhook", "purpose": "Вернуть amoCRM быстрый безопасный ответ" }
],
"connections": "Webhook → Build key → Insert key → Process once → Respond"
}
Если у вас уже есть отдельная таблица idempotency для webhooks других систем, используйте общий формат source + key + entity_type + entity_id. Тогда поддержку проще документировать.
Пошаговая настройка amoCRM webhook, n8n и Postgres ¶
- В amoCRM настройте webhook на production URL n8n и выберите только нужные события.
- В n8n импортируйте workflow JSON и подключите Postgres credential.
- Создайте таблицу
idempotency_keysили примените SQL из статьи. - Проверьте Code Node на событиях lead add, lead update и contact update, если они нужны.
- Настройте ветку: новая вставка запускает бизнес-действие, конфликт — возвращает “duplicate skipped”.
- Добавьте retention-политику: старые ключи можно удалять через 30–90 дней, если бизнес-процесс это допускает.
Тесты перед production и проверка повторных событий ¶
Отправьте один и тот же payload дважды. Первый запуск должен вставить ключ и выполнить бизнес-логику, второй — не должен делать повторное действие. Затем измените updated_at и убедитесь, что новое событие обрабатывается как отдельное.
curl -X POST "https://YOUR-N8N-DOMAIN/webhook/amocrm-webhook-deduplication" \
-H "Content-Type: application/json" \
--data @amocrm-webhook-deduplication-payload.json
Проверьте параллельный запуск: отправьте два одинаковых запроса почти одновременно. Если таблица настроена правильно, только один execution получит строку из RETURNING key.
Production-риски при обработке webhooks amoCRM ¶
- Ключ слишком грубый. Если использовать только
lead_id, все обновления одной сделки будут пропущены. - Ключ слишком детальный. Если включить случайные поля payload, повтор не распознается как повтор.
- SELECT перед INSERT. При параллельных событиях это создаёт гонку. Используйте unique insert.
- Бизнес-действие до записи ключа. Повтор уже успеет отправить сообщение или создать задачу.
- Webhook отвечает слишком медленно. Источник может повторить доставку, и нагрузка вырастет.
- Нет retention. Таблица idempotency растёт бесконечно и замедляет обслуживание.
Полезные ссылки и смежные workflow ¶
Официальная документация amoCRM описывает формат webhooks и настройку HTTP-адресов для событий. Для хранения ключей используйте Postgres на том же production-контуре, где размещён n8n.
Смотрите также внутри Nodbot:
- amoCRM в n8n — общая интеграционная страница.
- Postgres в n8n — подключение базы для idempotency.
- Webhook idempotency to Postgres — общий паттерн для любых источников.
- Tilda → amoCRM — защита лидов от дублей по телефону.
- Retry и DLQ для HTTP Request — обработка временных ошибок.
Критерии готовности ¶
- Одинаковый payload обрабатывается только один раз.
- Новое изменение той же сделки не пропускается как дубль.
- Параллельные одинаковые события не запускают два бизнес-действия.
- Postgres insert использует unique constraint и
ON CONFLICT DO NOTHING. - Webhook быстро отвечает источнику и не раскрывает внутренние ошибки.
- Есть retention-политика и владелец таблицы idempotency.
Нужна надежная CRM-автоматизация?
Nodbot настроит amoCRM webhooks, n8n, Postgres idempotency, retry/DLQ и мониторинг, чтобы одно событие не запускало бизнес-процесс дважды.