Перейти к содержанию

amoCRM webhook в n8n: дедупликация событий через Postgres idempotency key

Обновлено: 2026-05-30

Открыть мой план
Шаблон для внедрения

Импортируйте JSON в n8n, подключите Postgres credential и замените endpoint бизнес-обработки.

Проблема: amoCRM webhooks могут приходить повторно, приходить почти одновременно или запускать несколько downstream-действий на одно обновление сделки. Если workflow отправляет уведомление, создаёт задачу или синхронизирует данные без идемпотентности, одно событие превращается в два письма, две задачи и два внешних API-вызова.

Решение: webhook amoCRM в n8n должен сначала построить idempotency_key, затем атомарно записать его в Postgres с unique constraint и только после успешной вставки выполнять бизнес-логику. Повторное событие получает быстрый 200 OK, но не обрабатывается второй раз.

Это production-паттерн для интеграторов: импортируемый workflow JSON, SQL-таблица, Code Node, тест повторного payload и чек-лист запуска.

Схема дедупликации webhook amoCRM в n8n через Postgres
n8n принимает webhook amoCRM, строит idempotency key, вставляет его в Postgres и запускает бизнес-действие только один раз.

Проблема: почему amoCRM webhooks приходят повторно и ломают автоматизации

Webhook — это уведомление о событии, а не гарантия “ровно один раз”. CRM может повторить доставку, пользователь может быстро изменить одну и ту же сделку, а ваша логика может ответить медленно. Поэтому фильтр “я уже видел это событие” нельзя держать только в памяти n8n execution.

Особенно опасны действия с побочным эффектом: отправка сообщения клиенту, создание задачи менеджеру, списание бонуса, обновление внешней системы. Повтор такого действия выглядит как баг интеграции, хотя первопричина — отсутствие идемпотентной обработки.

Архитектура workflow для идемпотентной обработки

НодаРольЧто проверить
Webhook inputПринимает webhook amoCRMБыстрый ответ, HTTPS, секрет/allowlist при возможности
Build idempotency keyСобирает ключ из account/entity/action/timeКлюч стабилен для повтора и отличается для нового изменения
Insert idempotency keyПишет ключ в PostgresPRIMARY 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

  1. В amoCRM настройте webhook на production URL n8n и выберите только нужные события.
  2. В n8n импортируйте workflow JSON и подключите Postgres credential.
  3. Создайте таблицу idempotency_keys или примените SQL из статьи.
  4. Проверьте Code Node на событиях lead add, lead update и contact update, если они нужны.
  5. Настройте ветку: новая вставка запускает бизнес-действие, конфликт — возвращает “duplicate skipped”.
  6. Добавьте retention-политику: старые ключи можно удалять через 30–90 дней, если бизнес-процесс это допускает.
Журнал Postgres idempotency key для повторных webhook amoCRM
В журнале видно, какое событие было принято впервые, а какие повторы безопасно пропущены.

Тесты перед 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:

Критерии готовности

  1. Одинаковый payload обрабатывается только один раз.
  2. Новое изменение той же сделки не пропускается как дубль.
  3. Параллельные одинаковые события не запускают два бизнес-действия.
  4. Postgres insert использует unique constraint и ON CONFLICT DO NOTHING.
  5. Webhook быстро отвечает источнику и не раскрывает внутренние ошибки.
  6. Есть retention-политика и владелец таблицы idempotency.

Нужна надежная CRM-автоматизация?

Nodbot настроит amoCRM webhooks, n8n, Postgres idempotency, retry/DLQ и мониторинг, чтобы одно событие не запускало бизнес-процесс дважды.

Обсудить amoCRM webhook