RetailCRM и n8n: три рабочих сценария автоматизации заказов и склада без участия менеджера

Что даёт связка RetailCRM + n8n и зачем она нужна

RetailCRM держит фронт: заказы прилетают, менеджеры их обрабатывают, клиенты видят статусы. Но между CRM и остальным миром, складской системой, СДЭК, Boxberry, 1С, всегда остаётся зазор. Кто-то вручную копирует трек-номера из личного кабинета перевозчика в карточку заказа. Кто-то утром сверяет остатки по телефону со складом. Вот этот зазор и закрывает n8n.

Связка работает так: RetailCRM хранит бизнес-логику и общается с менеджерами, n8n обеспечивает интеграционный слой между CRM, складом и службами доставки. Webhook от RetailCRM прилетает в n8n, тот дёргает API склада, получает актуальный остаток, возвращает данные обратно в заказ. Или забирает трек-номер из API СДЭК и прописывает его в RetailCRM без единого клика менеджера.

С технической стороны здесь всё сложилось удачно. RetailCRM API v5 стабилен и хорошо задокументирован. Это позволяет строить автоматизации, которые будут жить без постоянного ремонта. Параллельно n8n (версия 1.x) научился нормально работать с HTTP-нодами, условиями и трансформацией JSON, не требуя писать кастомный код на каждый чих.

Про on-premise скажу отдельно, потому что это не просто "фича для параноиков". Если ваш магазин работает с физлицами и хранит ФИО, телефоны, адреса доставки, вы под 152-ФЗ. Облачный iPaaS означает передачу персональных данных третьей стороне, а это требует отдельного юридического оформления, согласий и иногда прямо невозможно по договору с маркетплейсом. n8n разворачивается на вашем сервере, данные не покидают контур. Это закрывает вопрос compliance без юридических костылей.

Конкретный список задач, которые решаются в первую очередь:

  • синхронизация складских остатков между WMS и RetailCRM по расписанию или триггеру
  • автоматическое создание отгрузок в СДЭК при смене статуса заказа
  • получение трек-номера от перевозчика и запись его в карточку заказа
  • уведомления менеджеру в Telegram, если заказ завис в статусе дольше X часов
  • передача данных о выкупах и отказах обратно в CRM для обновления статистики клиента

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

Схема потока данных: магазин передаёт заказы в RetailCRM, та отправляет задачи в n8n, который связывается со складом, CDEK и мессенджерами

Все три системы обмениваются данными через n8n как центральный маршрутизатор, а не напрямую друг с другом.

Архитектура интеграции: как это выглядит на практике

Если смотреть на схему целиком, получается три потока данных. Первый: RetailCRM отдаёт команду складу собрать заказ. Второй: склад сообщает, что заказ собран и готов к отгрузке. Третий: СДЭК получает заявку на забор, возвращает трек-номер, и этот номер оседает в RetailCRM. N8n стоит посередине и дирижирует всеми тремя.

Точка входа для первого потока, webhooks RetailCRM. Система умеет слать запросы на три типа событий: смена статуса заказа, создание заказа и изменение товарных позиций. Для интеграции со складом чаще всего нужен первый тип. Настраиваешь в RetailCRM webhook на статус «Подтверждён» (или как у тебя называется статус, после которого надо идти в сборку), указываешь URL вебхука в n8n, и дальше каждый раз при смене статуса n8n получает POST с телом заказа.

Дальше n8n решает, что делать с этим событием. Два варианта доставки задачи на склад, и выбор между ними зависит от того, что у тебя за WMS. Если склад умеет принимать REST, n8n делает HTTP-запрос напрямую: формирует тело с артикулами, количествами и ID заказа, шлёт в endpoint склада. Если склад работает через очередь сообщений, n8n подключается к RabbitMQ через встроенную AMQP-ноду и публикует сообщение в нужный exchange. Второй вариант лучше держит нагрузку: склад сам забирает задачи в своём темпе, и если WMS временно недоступна, сообщения не теряются.

Третий поток немного сложнее по структуре. Когда заказ готов к отгрузке, n8n обращается к СДЭК API v2 и создаёт заказ на доставку. В ответ приходит uuid, по которому потом нужно запросить трек-номер. Это отдельный запрос: СДЭК не отдаёт трек мгновенно, иногда проходит несколько минут. Так что здесь нужна либо пауза с последующим поллингом, либо отдельный scheduled-воркфлоу, который раз в N минут проверяет статус заказов без трек-номера.

Когда трек получен, n8n пишет его в RetailCRM через /api/v5/orders/{id}/edit. Поле для трека можно хранить в кастомном поле заказа или в стандартном delivery.trackNumber, если ты работаешь с объектом доставки напрямую. Я предпочитаю кастомное поле, потому что тогда виден весь путь: когда трек появился, какое значение было до этого, и можно добавить второй трек если посылка разделилась.

Схема выглядит линейной, но на практике в каждом потоке есть обработка ошибок. Webhook от RetailCRM мог прийти дважды (ретрай при таймауте), склад мог ответить 500, СДЭК мог вернуть ошибку валидации адреса. Про это отдельно.

Архитектурная диаграмма n8n с тремя дорожками: RetailCRM, склад и CDEK, двусторонние стрелки показывают обмен статусами и заказами

Двусторонние стрелки означают, что n8n не просто отправляет команды, но и получает подтверждения обратно в RetailCRM.

Настройка RetailCRM API: получение ключей и нужные эндпоинты

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

Где создать ключ. Заходите в «Настройки» → «Интеграция» → «API». Там кнопка «Создать API-ключ». Придумываете название (оно только для вас), выбираете методы. Для работы с заказами нужны четыре права: orders.read, orders.write, store.read, store.write. Меньше не даёте, больше не берёте без причины.

Сохранили ключ, скопировали строку. Второй раз она нигде не покажется, только пересоздавать.

Базовый URL выглядит так:

https://{ваш-домен}.retailcrm.ru/api/v5

Домен берёте из адресной строки вашего аккаунта. Версия v5 актуальна, v4 уже не стоит трогать: часть методов там задеприкейчена.

Три эндпоинта, которые покрывают 80% задач с заказами:

  • GET /orders, список с фильтрами по статусу, дате, менеджеру и ещё двум десяткам параметров
  • POST /orders/create, создание нового заказа
  • POST /orders/{id}/edit, обновление полей существующего

Кастомные поля чуть отдельная история. Сначала создаёте поле через интерфейс: «Настройки» → «Кастомные поля», указываете сущность (order, customer и т.д.) и код, например tracking_number. После этого поле доступно через эндпоинт /api/v5/custom-fields/order/tracking_number. Читаете и пишете через него же. Порядок важен: сначала интерфейс, потом API. Если пытаться создать поле через API без предварительной настройки, получите 404.

Пагинация. V5 работает через параметры page и limit. По документации API, за один запрос возвращается не более 100 записей. Если заказов больше, крутите page в цикле, пока в ответе не придёт pagination.totalPageCount.

Вот рабочий запрос: берём новые заказы, созданные с 1 мая 2026 года, по 50 штук на странице:

GET /api/v5/orders?filter[status]=new&filter[createdAtFrom]=2026-05-01&limit=50&page=1
X-API-KEY: your_api_key

Ключ передаётся именно в хедере X-API-KEY, не в теле и не в query-параметрах. Если передадите через URL, запрос формально пройдёт, но ключ осядет в логах сервера и nginx-е. Не делайте так.

Ответ придёт в JSON. Внутри: объект orders (массив), pagination с полями limit, totalCount, currentPage, totalPageCount. Этих четырёх чисел достаточно, чтобы написать нормальный цикл обхода без переплаты по запросам.

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

Начну с конкретики. RetailCRM умеет слать webhook на любое событие заказа. Нас интересует order.create. Как только статус заказа меняется на «новый», CRM делает POST-запрос на адрес, который мы указали в настройках интеграции. Этот адрес и есть наша Webhook-нода в n8n.

Тело запроса выглядит примерно так:

{
  "event": "order.create",
  "data": {
    "order": {
      "id": 12345,
      "status": "new",
      "items": [
        { "offerId": "SKU-001", "quantity": 2, "price": 1500 }
      ],
      "delivery": {
        "address": { "city": "Москва", "street": "Ленина", "building": "1" }
      }
    }
  }
}

n8n принимает его и сразу делает два дела в следующей ноде: извлекает позиции из data.order.items, адрес доставки из data.order.delivery.address и ID клиента. Всё это через простые выражения вида {{ $json.data.order.id }} прямо в интерфейсе без единой строки кода.

Дальше идёт HTTP Request-нода. Она формирует POST-запрос в складскую систему. Это может быть МойСклад, 1С с REST-обёрткой или собственный API. Структуру тела задаём сами под формат получателя. Главное, что n8n не просто пересылает исходный webhook, а трансформирует данные в нужную схему прямо внутри ноды через поле "Body Parameters" или JSON-режим.

Если склад принял задание и вернул ID сборки (скажем, { "taskId": "WH-9921" }), следующая нода записывает это значение в кастомное поле заказа через API RetailCRM. PATCH-запрос на /api/v5/orders/12345/edit с полем типа customFields[warehouse_task_id] = WH-9921. Теперь менеджер видит ID прямо в карточке заказа, не звоня на склад.

Параллельно (буквально параллельная ветка в том же workflow) Telegram Bot API получает сообщение вида: "Заказ #12345, Москва, ул. Ленина 1. Передан на склад, задание WH-9921". Ответственный менеджер видит это в личке или в групповом чате команды. Уведомление информационное, подтверждать ничего не нужно.

Теперь про ошибки. HTTP Request-нода в n8n поддерживает таймаут запроса. Ставим 10 секунд. Если склад не ответил, нода падает в ветку onError. Там два шага подряд: сначала сообщение в отдельный Telegram-чат с алертами ("Склад не ответил, заказ #12345, попытка 1"), потом Wait-нода на 5 минут и повторный HTTP Request. Количество попыток ограничиваем через счётчик в переменной workflow, чтобы не получить бесконечный цикл при реальном падении API склада.

Весь сценарий занимает 6-8 нод в n8n. Менеджер в процессе не участвует вообще. При нормальной работе всех API и сетевой связности задание попадает на склад очень быстро, за секунды, хотя реальная задержка зависит от времени отклика участвующих систем.

Холст n8n с узлами Webhook, Set, HTTP Request, IF и Telegram, соединёнными последовательной цепочкой

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

Второй сценарий: синхронизация остатков склада с RetailCRM

Склад живёт своей жизнью. RetailCRM живёт своей. И если их не синхронизировать каждые несколько минут, сайт будет принимать заказы на товары, которых физически нет, а менеджеры будут объяснять клиентам, почему "в наличии" оказалось ложью.

Задача решается в n8n за один воркфлоу. Разберу по частям.

Откуда берём данные. Большинство складских систем умеют одно из двух: отдавать остатки через REST-эндпоинт или выгружать файл (CSV, JSON). Оба варианта n8n закрывает стандартными нодами без кастомного кода. HTTP Request для REST, Read Binary File или FTP-нода для файлов, потом нода JSON или Spreadsheet File для парсинга. Ничего лишнего.

Расписание. Schedule Trigger, интервал 15 минут. Для большинства розничных магазинов этого достаточно. Если склад высокооборотный и 15 минут это катастрофа, ставь 5, но тогда следи за лимитами API RetailCRM на стороне магазина.

Маппинг артикулов. Это самое неприятное место. Склад хранит товары под своими идентификаторами, RetailCRM под своими, и они почти никогда не совпадают. Нода Code на JavaScript позволяет написать маппер прямо внутри воркфлоу, без внешних сервисов:

// n8n Code-нода: маппинг артикулов склада в offerId RetailCRM
const skuMap = {
  'ART-001': 'SKU-001',
  'ART-002': 'SKU-002'
};

return items.map(item => ({
  json: {
    offerId: skuMap[item.json.article] || item.json.article,
    quantity: item.json.stock,
    purchasePrice: item.json.cost
  }
}));

Если маппинг-таблица большая (сотни позиций), держи её в Google Sheets или PostgreSQL и подтягивай в начале воркфлоу отдельной нодой. Хардкодить 500 строк в Code-ноде, конечно, можно, но редактировать это потом будет больно. Кстати, задача двусторонней синхронизации данных между системами хорошо разобрана на примере amoCRM и Google Sheets с разбором типовых подводных камней: часть проблем с маппингом там универсальна.

Отправка в RetailCRM. Эндпоинт POST /api/v5/store/products/batch/edit принимает до 250 позиций за один запрос. Если товаров больше, перед HTTP Request ставь ноду Split In Batches с размером чанка 250. Каждый чанк уходит отдельным запросом.

Нулевые остатки. Это критичная точка, которую часто пропускают. Если quantity равен нулю, недостаточно просто передать 0. Нужно явно проставить status: 'not-available' в теле запроса, иначе RetailCRM может продолжать показывать товар как доступный. Добавь в Code-ноду проверку:

return items.map(item => ({
  json: {
    offerId: skuMap[item.json.article] || item.json.article,
    quantity: item.json.stock,
    purchasePrice: item.json.cost,
    status: item.json.stock === 0 ? 'not-available' : 'available'
  }
}));

Логирование. Каждый прогон должен оставлять след. После успешного batch-запроса пиши в Google Sheets или PostgreSQL: временную метку, сколько позиций обновлено, сколько ушло в not-available, был ли HTTP 200 или ошибка. Когда через неделю выяснится расхождение между фактическим складом и тем, что видит сайт, журнал позволит найти момент, где что-то пошло не так, за 2 минуты, а не за 2 часа ручной сверки.

Весь воркфлоу: Schedule Trigger → HTTP Request (склад) → Code (маппинг + статусы) → Split In Batches → HTTP Request (RetailCRM) → нода для логирования. Шесть нод. Работает.

Цикл синхронизации остатков: триггер по расписанию, маппинг артикулов, пакетное обновление в RetailCRM, запись лога, повтор через 15 минут

Расписание каждые 15 минут выбрано как баланс между актуальностью остатков и нагрузкой на API RetailCRM.

Третий сценарий: автоматическая передача заказов в СДЭК и получение трек-номеров

СДЭК API v2 работает по OAuth2. Без токена никаких запросов. Первым делом нода "Get Token" делает POST на https://api.cdek.ru/v2/oauth/token с grant_type=client_credentials и вашими client_id / client_secret из личного кабинета СДЭК. Токен живёт 3600 секунд, так что на каждый прогон workflow я обновляю его заново, а не кеширую.

// Нода: Get CDEK Token
// Method: POST
// URL: https://api.cdek.ru/v2/oauth/token
// Body (Form-Urlencoded):
grant_type=client_credentials
&client_id={{$env.CDEK_ID}}
&client_secret={{$env.CDEK_SECRET}}

CDEK_ID и CDEK_SECRET живут в Environment Variables n8n, не в теле workflow. Если воркфлоу когда-нибудь окажется в git-репозитории или коллеге на почте, учётные данные не утекут.

Готового коннектора СДЭК в n8n нет и в 2026 году не появилось. Только HTTP Request ноды. Это даже удобнее: полный контроль над заголовками и телом запроса без магии под капотом.

Создание заказа

После получения токена следующая нода формирует POST /v2/orders. Минимальное тело, которое СДЭК принимает без ошибок:

{
  "tariff_code": 137,
  "recipient": {
    "name": "Иванов Иван",
    "phones": [{ "number": "+79001234567" }]
  },
  "to_location": {
    "city": "Москва",
    "address": "ул. Ленина 1"
  },
  "packages": [
    {
      "number": "1",
      "weight": 500,
      "length": 20,
      "width": 15,
      "height": 10
    }
  ]
}

Тариф 137 (Посылка склад-дверь). Все реальные поля: имя, телефон, город, адрес, вес в граммах, габариты в сантиметрах. Заголовок Authorization:

Bearer {{$node["Get Token"].json.access_token}}

СДЭК ответит объектом с полем entity.uuid. Трек-номера в этом ответе ещё нет. Это особенность API: заказ создаётся асинхронно, и tracking_number появляется обычно через 1-5 минут.

Polling до получения трек-номера

Здесь подключается петля. В n8n я строю её через ноду Wait (2 минуты) и IF-проверку: если tracking_number пустой, возвращаемся на GET /v2/orders/{uuid}, иначе идём дальше.

// Нода: Check Tracking
// Method: GET
// URL: https://api.cdek.ru/v2/orders/{{$node["Create Order"].json.entity.uuid}}
// Headers: Authorization: Bearer {{$node["Get Token"].json.access_token}}

Ответ содержит entity.statuses и, когда СДЭК готов, entity.cdek_number (это и есть трек-номер). Прогонять polling больше 10 итераций смысла нет. Если за 20 минут трек не появился, нода отправляет уведомление в Telegram-чат менеджерам и завершает цикл с ошибкой.

Запись трек-номера в RetailCRM

Когда cdek_number получен, HTTP Request нода пишет его в кастомное поле заказа в RetailCRM через PATCH /api/v5/orders/{id}. Поле создаёте заранее в настройках CRM, тип string, код например cdek_track.

Отдельный момент: если заказы в RetailCRM поступают через API с полем delivery, убедитесь, что структура соответствует схеме нового модуля СДЭК. Старый модуль принимал плоский объект, новый ожидает вложенный deliveryService с кодом cdek. Путаница в схемах ломала интеграцию у нескольких команд, с которыми я разбирал этот кейс.

Уведомление клиенту

Трек-номер сразу уходит клиенту через следующую ноду. Не через менеджера, не "когда руки дойдут". SMS через смс.ру или письмо через SendGrid, шаблон с переменной {{tracking_number}} и ссылкой на отслеживание https://www.cdek.ru/ru/tracking?order_id={{tracking_number}}.

Менеджер об этом даже не думает. Workflow делает всё само.

Петля опроса CDEK: получить трекинг-номер, проверить статус, при отсутствии ответа повторить, при изменении записать в RetailCRM и уведомить клиента

Retry-логика защищает от временных сбоев API CDEK и не требует ручного перезапуска сценария.

Обработка ошибок и мониторинг флоу в production

Когда флоу падает в три часа ночи, узнать об этом нужно до того, как клиент напишет в поддержку. В n8n для этого есть Error Workflow: отдельный флоу, который автоматически запускается при любой необработанной ошибке в основном. Настраивается в Settings каждого флоу, буквально одно поле.

Минимальная конфигурация Error Workflow, которую я использую: Telegram-нода отправляет три вещи. Название упавшей ноды (из {{ $json.execution.error.node.name }}), текст ошибки и прямую ссылку на конкретное выполнение в n8n UI. По ссылке открываешь, видишь входные данные ноды в момент падения. Разбор занимает две минуты, а не двадцать.

Все HTTP-запросы к RetailCRM и СДЭК оберните в Try/Catch. Нода появилась в n8n 1.x и решает конкретную проблему: если из пакета 30 заказов один упал с 500-й ошибкой API, флоу не останавливается полностью. Ошибочный заказ уходит в ветку Catch, остальные 29 обрабатываются дальше. Без Try/Catch один кривой ответ от СДЭК кладёт весь batch.

Отдельная история с идемпотентностью при передаче в СДЭК. Сеть нестабильна, n8n может ретрайнуть запрос, и заказ создастся дважды. Защита простая: перед вызовом API СДЭК ищите uuid заказа в кастомном поле RetailCRM. Если поле уже заполнено, пропускаете создание и идёте дальше. Uuid пишите в кастомное поле сразу после успешного ответа от СДЭК, в том же выполнении. Это не теория, я видел дубли заказов