Документация
Интеграция с учётной системой
Как подключить обмен и что вызывать. Уровни независимы: подключайте в любом составе и порядке. Действующая редакция — обновляется вместе с платформой.
Содержание
Обзор интеграцииПлатформа не требует ни доступа в вашу сеть, ни изменения конфигурации: обмен идёт исходящими HTTPS-запросами внешней обработки.
Как устроен обмен
Мы — сервер, ваша система — клиент. Все запросы исходящие, с вашей стороны; мы к вам не подключаемся.
┌────────────────────┐ исходящие HTTPS-запросы ┌──────────────────┐
│ ВАША СИСТЕМА (1С) │ ─────────────────────────────────────► │ LOTUS MARKET │
│ = клиент │ ◄─────── только ответы на них ────── │ = сервер │
└────────────────────┘ └──────────────────┘
Опросом забирается только одно — заказы: это единственное событие, которое происходит на нашей стороне. Обо всём остальном (готовность, недостача, остаток, поставка) ваша система знает сама в момент проведения документа и просто отправляет нам.
Кто что делает
| Роль | Задачи |
|---|---|
| Владелец аккаунта на платформе | Выпускает ключ, настраивает права, сопоставляет склады и уже выложенный товар, отвечает на вопросы кабинета. Программист для этого не нужен |
| Разработчик | Пишет на вашей стороне модуль обмена — внешнюю обработку, которая ходит в наш API |
Подготовка
Это общая часть: она нужна для любого уровня.
Ключ доступа
Ключ выпускает владелец аккаунта в кабинете: Профиль → Интеграции → Подключённые системы → Выпустить ключ. Сотруднику компании это действие недоступно при любых правах.
- Для выпуска профиль компании должен быть подтверждён — почта, ИНН и банковские реквизиты. Иначе
403 PROFILE_INCOMPLETEсо списком недостающих полей. - Если к аккаунту уже подключён МойСклад, выпуск отвечает
409 INTEGRATION_CONFLICT: место учётной системы одно. Выпустить ключ можно только «с заменой» — МойСклад при этом отключается. - Значение вида
lm_…(67 символов) показывается один раз; не сохранили — только выпуск нового. - Ключ бессрочный, действует до отзыва. Для тестового и боевого контуров ключи разные.
- Активных ключей у аккаунта до 10: так делается ротация без простоя и разводятся разные экземпляры вашей системы.
- Права выпущенного ключа владелец меняет без перевыпуска — новые действуют со следующего запроса.
- В списке систем видны первые 12 символов ключа и колонка «Последнее обращение» — самая быстрая проверка, что обмен пошёл.
Права ключа
Права нового ключа по умолчанию:
| Раздел в кабинете | Уровень по умолчанию |
|---|---|
| Заказы | Просмотр |
| Товары и партии | Полный |
| Поставки | Полный |
| Рекламации · Склады и настройки · Дашборд и обороты | Нет доступа |
Включённые действия: «Выдача заказов», «Отклонение и недостачи», «Внесение товара», «Изменение цен», «Списание остатков».
Действиям над заказами достаточно уровня «просмотр»: полный по разделу «Заказы» им не нужен, а выдав его, владелец попутно открывает ключу выгрузку контрагентов с персональными скидками.
Рекламации учётной системе недоступны в принципе: методов для них в API нет, и выданное право ничего не откроет.
Адреса и тестовый контур
Тестовый контур изолирован от боевых заказов — экспериментировать в нём можно свободно. Боевой адрес в настройках обработки должен появляться осознанно, а не «по умолчанию». Базовые адреса обоих контуров — в общих правилах.
Что меняется в кабинете при живом ключе
Это стоит знать до включения обмена — перечисленное наступает от самого факта живого ключа, независимо от того, какие уровни вы подключили.
- Загрузка прайса из Excel закрывается (
409 IMPORT_DISABLED_BY_INTEGRATION): у остатка должен быть один источник. Закрыты загрузка файла и подтверждение импорта; уже начатый черновик доредактировать можно. - Количество партии с вашим кодом больше не правится руками в кабинете (
409 QUANTITY_OWNED_BY_INTEGRATION): его ведёт обмен. Цена и размер связки остаются доступными. Отзыв ключа снова открывает поле. ⚠️ Замок стоит на поле количества, но не на кнопке быстрого списания «Товар ушёл»: нажать её можно, только результат перетрёт ближайшая выгрузка. Списывать остаток надо в своей системе. - Появляется плашка расхождений остатка. Если товар продан и на платформе, и вне её, платформа закрывает витрину по этой позиции и показывает поставщику список затронутых заказов — но ничего не отменяет и не оформляет сама. Кому из покупателей достанется остаток, решает поставщик. Та же плашка появляется, когда позиция снята по отсутствию в выгрузке, а невыданные заказы по ней остались: расчёт тот же, что у явного нуля.
- Работает одна учётная система. Подключить одновременно 1С и МойСклад нельзя: попытка вернёт
409 INTEGRATION_CONFLICTс предложением заменить систему. - Новые товары выходят на витрину после одобрения. Позиция, код которой платформа видит впервые, создаётся скрытой и попадает в очередь: Подключённые системы → Новые товары. Там поставщик проверяет фотографию, название и категорию и нажимает «На витрину». Обмен это не останавливает: метод отвечает
200, остаток и цена применяются, следующие выгрузки продолжают обновлять числа — товар просто не виден покупателям до решения человека. Уже связанные позиции одобрения не требуют никогда. Правило снимается тумблером «Автоматическая связка» в том же кабинете; по умолчанию он выключен. Тот же список виден и в разделе «Мои товары» — вкладка «Не на витрине», чип «Ждут проверки 1С»; массовое «Показать» такие позиции пропускает и сообщает, сколько их осталось.
Сопоставление складов
Коды ваших складов сопоставляются с точками выдачи в кабинете (Подключённые системы → Склады), один к одному. Это нужно всегда, даже при единственном складе: код склада — обязательное поле каждой строки, и автоматической привязки к единственной точке выдачи нет. Пока код не сопоставлен, строки остатка по нему отклоняются с UNKNOWN_WAREHOUSE, а документ поставки отвергается целиком.
Если точку выдачи удалили на платформе, её код для обмена становится несопоставленным: строки остатка по нему отклоняются с UNKNOWN_WAREHOUSE, а документ поставки отвергается тем же отказом, что и код без сопоставления. Саму связь удаление точки не стирает: пока код не перенесли на другую точку или не убрали из сопоставления, лента заказов по-прежнему называет его у заказов удалённой точки.
Список наших складов через API не отдаётся, и наоборот — справочник складов ваш, мы его не видим. Код вводится руками; какой именно код не сопоставлен, видно в ответе метода.
Уровень 1 — Остатки и цены
Что даёт. Витрина показывает тот остаток, который действительно есть, и ту цену, которая стоит у вас. Меньше недостач, меньше отказов покупателю.
Что понадобится. Стабильный код номенклатуры у каждой позиции, сопоставленные склады, сопоставленный уже выложенный товар (см. ниже) и осознанно выбранный режим чтения выгрузки в кабинете.
Метод. Один: Передача остатков и цен.
Схема выгрузки зависит от режима чтения. По умолчанию стоит режим «весь остаток»: каждый пакет читается как полный снимок склада, и прайс идёт целиком в каждой выгрузке. Дельта по событию плюс полная сверка по расписанию — схема для режима «только изменения», и переключить его нужно ДО смены регламента: в режиме по умолчанию дельта снимает с витрины всё, чего не оказалось в двух выгрузках подряд, и часовая сверка от этого не спасает. Подробности — в карточке метода.
Что важно учесть
- Товар, выложенный на платформе до подключения, вашего кода не имеет: коды закрепляются за позициями в кабинете. Строка с новым кодом, похожая на такую несвязанную позицию того же склада, не применяется — в кабинете появляется предложение связать их. Непохожая строка предложения не породит: рядом с существующей заведётся вторая позиция.
- ⚠️ Один код — одна позиция на складе. При ручной привязке платформа не мешает вписать один код двум партиям одного склада, если обе принадлежат одной карточке товара; отказа в этот момент не будет, а первая же выгрузка по такому коду начнёт возвращать
AMBIGUOUS_EXTERNAL_CODE. - Название и категорию платформа требует, только когда ещё не знает карточку товара под этим кодом — то есть при первом появлении самого кода. Если карточка уже есть, позиция заводится и без них: выгрузка по второй точке, возврат кода после удаления позиции, код, живший до этого только в поставке. У уже заведённой позиции они игнорируются.
- ⚠️ Пропавшая из выгрузки позиция считается распроданной после двух выгрузок подряд без неё — если вы присылаете весь остаток (режим по умолчанию). Присылаете только изменения — переключите режим в кабинете («Подключённые системы → Системы → Как читать выгрузку остатков») и снимайте распроданное строкой с нулём. Пауза выгрузок ничего не снимает, а вывод делается по каждому складу отдельно. Склад, по которому за двое суток не набирается трёх выгрузок, снимается по отсутствию, только если и остальные склады выгружаются так же редко: рядом с часто выгружаемыми складами распроданное на нём обнуляйте строкой с нулём.
- До 5 000 строк — одним пакетом. Прайс в эти пределы укладывается целиком, делить его на страницы не нужно; пакет больше 5 000 строк отвергается целиком.
- ⚠️ Страницы одного прайса идут подряд, с паузой не больше 5 минут — и с запасом: паузу платформа меряет от окончания обработки одной страницы до окончания обработки следующей, а страница с новыми позициями обрабатывается дольше обычной. Паузу длиннее платформа читает как конец одной выгрузки и начало следующей, а в режиме «весь остаток» это значит, что позиции недосланных страниц она сочтёт пропавшими и снимет с витрины. Опасен ровно один случай — три и более страницы с долгими паузами: прайс, разложенный на четыре пакета с паузой в шесть минут, каждый цикл теряет с витрины около половины позиций, и возвращают их следующие страницы. Две страницы безопасны при любой паузе. Строки одного склада держите подряд, на соседних страницах, — отсортируйте выгрузку по складу: страницы платформа склеивает по складу, и у маленького склада, строки которого разбросаны по всему прайсу, между его страницами паузы дольше 5 минут. Пока весь прайс уходит меньше чем за час, такой склад прикрывает общий ритм выгрузок; но выгрузка, растянутая на час и дольше (или идущая на фоне складов вразбежку), общего ритма не имеет — и такой склад теряет часть позиций на каждой своей странице, а следующая их возвращает, в каждом цикле. Прерванную выгрузку — пауза по
Retry-After, серия5xx, перезапуск задания — отправляйте заново с первой страницы, а не дописывайте хвостом. И учтите: присутствие ведётся по паре «поставщик и склад», ключи в нём не различаются — два задания или два экземпляра вашей системы, делящие один склад между собой, читаются как страницы одной выгрузки. - Один запрос заводит не больше 150 новых позиций. Остальные строки с новыми кодами отвечают построчным
NEW_POSITIONS_LIMIT— это не ошибка: в режиме «весь остаток» следующая выгрузка заведёт следующие 150, а остатки и цены уже заведённых позиций применяются в каждой выгрузке целиком. Большой прайс поэтому заводится за несколько выгрузок: 5 000 новых позиций при выгрузке раз в 10 минут — примерно за 6 часов. В режиме «только изменения» отложенные строки присылают снова. Места в потолке не занимают строки с ошибкой в данных (нет названия, неизвестная категория) — ошибку платформа называет сразу — и строки с кодом, по которому уже ждёт решения предложение «похоже на вашу позицию»: пока строка по-прежнему похожа на позицию из предложения, она сразу получает тот же отказ. Новый отказ «похоже» занимает место один раз. - Первая выгрузка после включения режима «весь остаток» снимает накопившийся хвост позиций, которых на складе давно нет, — у живого поставщика это бывает больше половины ассортимента склада. Это ожидаемо; отправляйте её полной. Если в ней не окажется больше 90 % позиций склада, платформа сочтёт выгрузку сломанной и не снимет ничего. Это касается и склада, который выгружается своим пакетом или подключён позже других, если его первый прогон пришёлся на первые двое суток после первого прогона поставщика, а сам склад выгружается не реже раза в 10 часов. Позже по складу работает обычный предохранитель; чтобы снять хвост склада, подключённого позже, поставщик ещё раз выбирает в кабинете «Присылаете весь остаток» — выбор возвращает первый прогон всем складам — и следующая выгрузка уходит полной.
- Предохранитель. Если по складу пропало больше 20 позиций и это больше половины его позиций с вашим кодом и ненулевым остатком, снятие по складу пропускается целиком — вкладка «Здоровье» показывает строку «Снятие пропавших позиций останавливалось предохранителем» с числом недосчитанных позиций и временем. Сам пропуск действует на ту выгрузку, где сработал: следующая нормальная выгрузка снимает как обычно, — а вот предупреждение на панели держится, пока повторов нет, и не меньше суток (у редких выгрузок — четыре окна вывода, то есть дольше), чтобы разовый сбой не потерялся. Поэтому «ничего не снялось» — не всегда «механика не работает».
- В ответе приходят только ошибочные строки, с номером строки в запросе (нумерация с нуля).
Уровень 2 — Заказы
Что даёт. Заказы попадают в вашу систему документами; менеджер перестаёт переносить их руками. Покупатель узнаёт о недостаче заранее, а не приехав на склад.
Что понадобится. Наши идентификаторы заказа и его позиций из ленты — это ключи всех последующих операций.
Методы. Проверка изменений и лента заказов; шесть действий над заказом: готов к выдаче, откат готовности, отклонение заказа, недостача по позиции, восстановление, удаление позиции.
Что важно учесть
- Опрос идёт в два шага: лёгкая проверка изменений, а при положительном ответе — постраничный обход ленты. Момент следующего опроса платформа отдаёт сама, полем
serverTime, а интервал — полемnextPollAfterSeconds; собственные часы для этого не годятся: их расхождение с сервером приводит к пропуску заказов. - ⚠️
304означает «ответ тот же, что в прошлый раз», а не «изменений нет». Тела в нём нет вовсе, а в сохранённом прошлом ответеhasChangesвполне может стоятьtrue— те же изменения ещё не разобраны. - Лента отдаёт изменения, а не только новые заказы. Один и тот же заказ приходит много раз — при отмене, недостаче, получении, перечислении денег, а иногда и без видимых изменений, — и каждый раз целиком, со своим текущим состоянием.
Уровень 3 — Поставки и предзаказы
Что даёт. Будущая поставка выкладывается на витрину из вашего документа, и покупатели оформляют по ней предзаказы — товар продаётся до того, как физически приехал. При приходе предзаказы сами становятся обычными заказами.
Что понадобится. Номер вашего документа поставки (он же защита от дублей), коды номенклатуры и код склада прихода.
Методы. Четыре: создание, изменение, приход, отмена.
Что важно учесть
- Один ваш документ = один вызов: номер, склад, ожидаемая дата и все позиции разом (до 500, коды не повторяются). Номер документа же защищает от дублей.
- Изменение приводит состав к переданному: новые позиции добавляются, отсутствующие убираются, совпадающие обновляются.
- Приход превращает предзаказы в обычные заказы и ставит товар в наличие. Отмена требует причины — её увидят покупатели с предзаказами.
- ⚠️ Опубликованная поставка закрывается платформой сама в конце дня
expectedAtпо московскому времени, не дожидаясь сообщения о приходе. Задержка оформляется переносом даты через изменение поставки.
Уровень 4 — Контрагенты и скидки
Что даёт. Ваши постоянные клиенты и их персональные скидки переезжают на платформу. Когда клиент с этим ИНН регистрируется, платформа узнаёт его и применяет вашу скидку сама — покупателю не приходится просить её, а вам вспоминать, кому что обещано.
Что понадобится. Расширенные права ключа — см. «Права ключа». И список контрагентов с ИНН.
Метод. Один: Контрагенты.
Эксплуатация
Журнал обмена
Кабинет, Подключённые системы → Журнал обмена: метод, адрес, код ответа, код и текст ошибки, а по пакетным методам — число принятых и отклонённых строк; у выгрузки остатков к ним добавляются «отложено N» — строки за потолком новых позиций, которые отказом не считаются, — и «снято по отсутствию N», когда снятие было. Хранится 30 суток и переживает отзыв ключа.
⚠️ Построчных причин отказа в журнале нет — только счётчики. Причина по каждой строке приходит один-единственный раз: в теле ответа на сам запрос.
Часть запросов отсекается раньше журналирования и в журнал не попадает вовсе:
| В журнале есть | В журнале нет |
|---|---|
Отказы методов, 409 по идемпотентности, 429 по потолку проверки изменений (20/мин) |
Успешные опросы изменений, 401, 429 по потолку ключа и по IP-адресу, 403 «метод недоступен ключу интеграции» |
Поэтому при шторме запросов журнал выглядит пустым, хотя запросы доходят.
Вкладка «Здоровье»
Кабинет, Подключённые системы → Здоровье: одна страница про то, что из обмена реально работает. Только чтение — панель ничего не запускает и не чинит, она называет проблему и ведёт на вкладку, где та чинится.
| Строка | Что показывает |
|---|---|
| Остатки и цены | когда была последняя выгрузка и с каким обычным ритмом они идут; если тишина затянулась против вашего же ритма — строка краснеет |
| Снятие по отсутствию | сколько позиций снято за сутки, потому что перестали приходить в выгрузке, и не приостановлено ли снятие (режим «только изменения», нет галочки «Списание остатков», ритм ещё не набран, остатки склада идут без перерыва, выгрузки слишком редкие, сработал предохранитель). Отдельная строка «Позиции снимаются и появляются по кругу» означает, что за сутки снято больше позиций, чем их у вас всего, — так выглядит прайс, приходящий несколькими запросами с паузами |
| Отложенные строки | сколько строк последней выгрузки отложено потолком новых позиций за запрос. Серая строка, не проблема: большой прайс заводится частями, и в режиме «весь остаток» эти строки заведутся следующими выгрузками |
| Отклонённые строки | сколько строк отклонила последняя выгрузка и разбор причин за неделю: несопоставленный склад, «похоже на существующий товар», ошибки формата. Отложенные сюда не входят |
| Заказы | когда лента забиралась в последний раз и сколько заказов ждёт выгрузки. Если лента не забиралась ни разу — строка серая «не забиралась»: значит, уровень заказов не подключён |
| Опрос изменений | когда метод проверки изменений отвечал успешно и сколько было неудачных вызовов |
| Что мешает выгрузке | отказы 403 за неделю: неподтверждённый профиль, ограничение размещения за неоплаченный счёт, режим завершения дел (владелец отказался от условий платформы) |
| Контрагенты | когда выгружался справочник |
| Очередь одобрения | сколько новых товаров ждёт проверки |
⚠️ «Последнее обращение» в списке систем и «Здоровье» — про разное. Первое двигает ЛЮБОЙ запрос: выгрузка остатков каждые десять минут держит его зелёным, даже если ленту заказов ваша обработка не читает неделями. «Здоровье» ведёт отметки по каждой ветке отдельно — именно поэтому оно и появилось.
Отдельно: если ветка заказов уже работала, а потом замолчала и накопились заказы старше трёх часов, платформа один раз присылает поставщику уведомление «1С не забирает заказы». Повторно оно не приходит — сигнал гаснет при первом же успешном заборе ленты.
Смена и отзыв ключа
- Отзыв мгновенный: следующий же запрос получает
401. - Ротация без простоя: выпустить новый ключ → прописать → отозвать старый.
- Учётная система одновременно одна; при переходе на другую платформа предложит заменить.
- Сопоставления складов, товаров и внешние коды при отключении сохраняются: возврат стоит одного ключа, а не повторной настройки.
Если что-то пошло не так
- Тело ответа — код и причина отказа, включая построчные.
- Вкладка «Здоровье» — какая именно ветка обмена молчит и что ей мешает.
- Журнал обмена в кабинете — дошёл ли запрос и сколько строк принято.
- Тестовый контур и чек-лист проверки подключения — он изолирован от боевых заказов, ломать в нём ничего не жалко.
На время работ у вас есть прямой контакт нашего разработчика.
Общие правила
Действуют во всех методах. Каждое правило выписано здесь один раз, а в карточке метода стоит его выжимка — чтобы разобраться в методе, уходить со страницы не нужно.
Адреса
Пути в справочнике указаны относительно базового адреса. Только HTTPS, TLS 1.2 и выше.
Боевой адрес в настройках обработки должен появляться осознанно, а не «по умолчанию»: ключ одного контура на другом не работает, и первая же выгрузка мимо контура уходит не туда.
| Контур | Базовый адрес |
|---|---|
| Тестовый | https://stg.lotusmarket.ru/api/v1 |
| Боевой | https://lotusmarket.ru/api/v1 |
Аутентификация — заголовок X-Api-Key
Ключ бессрочный, действует до отзыва и показывается один раз при выпуске. Для тестового и боевого контуров ключи разные. Активных ключей у компании до 10 — этим и делается ротация без простоя.
Права ключа — уровень по разделу плюс отдельные галочки действий; уровень и галочка проверяются независимо. По умолчанию: «Заказы» — просмотр, «Товары и партии» и «Поставки» — полный; включены действия «Выдача заказов», «Отклонение и недостачи», «Внесение товара», «Изменение цен», «Списание остатков». Владелец меняет права без перевыпуска — новые действуют со следующего запроса.
Сверх прав все изменяющие методы требуют подтверждённого профиля владельца (почта, ИНН, банк) — иначе 403 PROFILE_INCOMPLETE со списком недостающих полей. Чтения этого не требуют.
⚠️ 403 PERMISSION_DENIED «метод недоступен ключу интеграции» — не про матрицу прав, а про адрес: такого пути в машинной ветке нет, и расширение прав не поможет. На закрытом контуре (сегодня это тестовый) тот же неверный адрес отвечает 403 SITE_LOCKED ещё до проверки ключа.
Ключ не является пользователем — в частности, публикация новой редакции юридических документов обмен не останавливает.
Формат запросов и ответов
GET несёт только заголовки. POST — плюс Content-Type и Idempotency-Key, тело JSON в UTF-8.
Тело, не разобравшееся как JSON (обрезанное; валидный JSON, который объектом не является — массив, число, строка; а также пустое там, где тело обязательно), даёт 400 BAD_REQUEST у методов, которые тело используют. null телом-не-объектом не считается: он читается как «значения нет», ровно как пустой объект.
Непустое тело принимается только с Content-Type, оканчивающимся на json. Любой другой тип — 400 BAD_REQUEST, и проверка идёт до разбора, то есть до любых сообщений о полях. Гард стоит на методах, которые тело используют; «Готов к выдаче» и «Откат готовности» принимают что угодно. Пустой POST без Content-Type законен везде, где тело необязательно.
Читает метод тело или нет, в слепок идемпотентности оно входит всегда: повтор отправляйте байт в байт тем же телом — впервые добавленные фигурные скобки вместо пустого тела дадут 409.
Успешный ответ приходит в обёртке data (плюс meta у списков), ошибка — объектом error с полями code и message.
HTTP-коды
Повторим ли запрос, видно по коду ответа: разбирать текст сообщения для этого не нужно.
⚠️ Повторы 429 и 5xx безопасны сами по себе, но не в середине выгрузки остатков, идущей несколькими страницами: пауза дольше 5 минут между пакетами одного прайса читается как конец выгрузки, и в режиме «весь остаток» позиции недосланных страниц уходят с витрины. Если повторы затянулись, отправьте прайс заново с первой страницы — правило страниц описано в схеме выгрузки.
| Код | Что случилось | Поможет ли повтор |
|---|---|---|
| 200 | Успех | — |
| 201 | Объект создан (создание поставки) | — |
| 304 | Данные не менялись (ответ на If-None-Match) | Тела нет: годен сохранённый прошлый ответ |
| 400 | Некорректный запрос: битый JSON, некратное количество, неверный формат | Нет |
| 401 | Ключ отсутствует, неверен или отозван | Нет: ключ меняется в кабинете |
| 403 | Не хватает прав ключа, метод недоступен ключу, не заполнен профиль, аккаунт удалён либо приостановлено размещение нового | Нет: решается в кабинете |
| 404 | Объект не найден или в нём нет ваших позиций. Код HTTP_ERROR вместо NOT_FOUND — опечатка в адресе | Нет |
| 405 | Неверный HTTP-метод для этого адреса (код HTTP_ERROR) | Нет |
| 409 | Конфликт состояния (см. описание метода) | По описанию метода. Исключение: IDEMPOTENCY_IN_PROGRESS — да, тем же ключом через паузу |
| 413 | Тело запроса больше 20 МБ (код HTTP_ERROR) | Нет: пакет разбивается на меньшие |
| 429 | Превышена частота | Да, после паузы Retry-After секунд |
| 500, 503 | Ошибка на стороне платформы | Да |
Идемпотентность — заголовок Idempotency-Key
Заголовок обязателен в каждом POST; без него — 400. Длина до 200 символов. Изменение поставки (PUT) принимается и без него: там от дублей защищает номер вашего документа.
На каждую новую операцию — новый ключ (случайный UUID), генерирует ваша сторона. Повтор после сбоя отправляйте с тем же ключом: если первая попытка дошла, действие не выполнится второй раз, вернётся сохранённый ответ с исходным кодом, включая 201.
Повтор с тем же ключом, но другим телом даёт 409. Слепок считается по методу, пути и телу — у всех методов, включая те, что тело не читают.
409 с кодом IDEMPOTENCY_IN_PROGRESS («запрос с этим Idempotency-Key ещё выполняется») означает, что первая попытка не завершилась. Это единственный 409, который НУЖНО повторить — тем же ключом, через паузу. Опознавайте случай по машинному коду, а не по русскому тексту сообщения.
Незавершённый резерв старше 5 минут отвечает обычным 409 CONFLICT с текстом «Исход первой попытки неизвестен». Так бывает, когда процесс не дожил до записи исхода: действие могло примениться, а могло и нет. Гарантия «действие не выполнится второй раз» держится на записанном исходе, поэтому здесь платформа не угадывает — сверьтесь с лентой заказов и, если действия не было, отправьте его с НОВЫМ ключом. Повторять тем же ключом бесполезно: ответ будет тот же. Автоматического освобождения зависшего резерва нет.
Частично неуспешный ответ тоже сохраняется. Пакет, вернувший accepted: 48, rejected: 2, при повторе с тем же ключом вернёт тот же ответ и ничего не применит: исправленные строки отправляйте с НОВЫМ ключом. Такой пакет — выполненный запрос с ответом 200, а не отказ.
Ключи хранятся на платформе 7 суток и привязаны к конкретному ключу доступа: после его ротации прежние ключи идемпотентности начинают новые операции.
Частота запросов
Ограничения применяются одновременно, поэтому фактический потолок — минимум из применимых. Типовое внедрение (вся 1С ходит с одного внешнего адреса) упирается в 120 запросов в минуту, а не в 300; 300 достижимы, только если обмен идёт с нескольких адресов.
Превышение — 429 с заголовком Retry-After: выдержать паузу и повторить.
⚠️ Пауза повтора спорит с правилом страниц в выгрузке остатков, и спор решается в пользу страниц. Пауза дольше 5 минут между пакетами одного прайса читается платформой как конец выгрузки и начало следующей, а в режиме «весь остаток» это снимает с витрины позиции недосланных страниц. Поэтому страницу, повтор которой пришлось отложить надолго, не дошлите в хвост: считайте выгрузку прерванной и отправьте прайс заново с первой страницы. То же и с повторами 5xx.
Интервал опроса задаёт сервер — поле nextPollAfterSeconds в ответе и заголовок X-Next-Poll-After (заголовок нужен потому, что у 304 тела нет). Значение меняется само: 30 секунд при активности, 60 после недавних заказов, 300 в тишине, 900 ночью; платформа может разредить опрос глобально. Читайте его из каждого ответа, не зашивайте в код.
Это нижняя граница, а не расписание. Реже указанного ходить можно всегда: ничего не теряется, заказ просто появится у вас позже. Чаще — нельзя, и следит за этим только потолок в 20 запросов в минуту.
Отсюда ответ для систем с фиксированным расписанием регламента (типовой случай 1С): опрос раз в минуту подходит при любом значении поля — это 1 запрос из 20. Чтобы при этом следовать интервалу и не ходить впустую ночью, сделайте запуск раз в минуту тиком, а не опросом: сохраняйте из ответа момент следующего опроса и в начале задания выходите, если он ещё не наступил.
| Ограничение | На что считается |
|---|---|
| 20 запросов в минуту | Проверка изменений, по ключу доступа |
| 300 запросов в минуту | Весь машинный обмен, по ключу доступа |
| 120 запросов в минуту | Любые запросы с заголовком X-Api-Key с одного IP-адреса, включая неудачные попытки аутентификации |
Пагинация
Списки отдаются страницами; в meta приходит курсор. Значение непрозрачно: передавайте его как есть, не разбирайте.
Цикл обхода: запросить страницу с since, обработать data, при meta.hasMore = false закончить, иначе повторить запрос с cursor вместо since.
Ограничение размещения за неоплаченный счёт
Комиссия платформы в ценах API не фигурирует — платформа выставляет на неё отдельный счёт вне API. Если счёт не оплачен в срок, оператор вправе приостановить поставщику размещение нового: товары скрываются с витрины покупателей, а выгрузка новых позиций отклоняется. Ограничение снимается платформой после оплаты.
Отвечают 403 BILLING_RESTRICTED: остатки и цены — всегда, весь пакет целиком, построчного results в этом случае нет; создание поставки — всегда; изменение поставки — только если документ размещает новое (добавляет позицию, увеличивает количество существующей либо выводит на витрину поставку, которую покупатели ещё не видели).
У черновика размещением считается любой переданный publishAt, даже совпадающий с сохранённым; у запланированной к показу — только перенос момента, повтор того же момента отказа не вызывает.
Продолжает работать всё остальное: лента заказов и все действия по ним, откат готовности, приход и отмена поставки, а также изменение поставки, если документ только убирает позиции, уменьшает количество или меняет цены. Наведение порядка в поставке ограничением не закрыто.
Ограничение снимается оплатой счёта — со стороны обмена оно не лечится.
Режим завершения дел
Обязательные документы платформы принимает ЧЕЛОВЕК, и ключ интеграции за него этого сделать не может — поэтому обмен не останавливается из-за неподписанной новой редакции. Но если владелец ответил отказом явно, аккаунт переходит в режим завершения дел: он живёт ещё 30 дней, чтобы довести начатое, после чего закрывается.
В этом режиме отвечают 403 ACCOUNT_WIND_DOWN: остатки и цены (весь пакет целиком, построчного results в этом случае нет), создание поставки, контрагенты и их скидки; изменение поставки — только если документ размещает новое (добавляет позицию, увеличивает количество существующей либо выводит на витрину поставку, которую покупатели ещё не видели).
Продолжает работать всё остальное: опрос изменений, лента заказов и все действия по ним, откат готовности, приход и отмена поставки, а также изменение поставки, если документ только убирает позиции, уменьшает количество или меняет цены. Смысл ровно тот же, что и у ограничения за неоплаченный счёт: закрыто размещение нового, а не работа по обязательствам, которые уже есть.
Со стороны обмена режим не лечится: его снимает владелец, приняв условия в личном кабинете. Автоповторы бессмысленны — сообщите поставщику. Если срок истёк и аккаунт закрылся, ключ начинает отвечать ACCOUNT_DEACTIVATED.
Идентификаторы
UUID в текстовом виде: 018f3b2a-7c1e-7b2a-9f00-1a2b3c4d5e6f. Регистр не значим.
Дата и время
RFC 3339 со смещением. Сервер принимает любое смещение, но настоятельно рекомендуется UTC с суффиксом Z — так журналы обеих сторон сопоставимы без пересчёта. Правильно: 2026-08-04T09:14:02Z. Неправильно: 04.08.2026 12:14:02 или 2026-08-04T12:14:02 без зоны.
⚠️ В ответах момент может нести дробную часть секунды: serverTime — до наносекунд, createdAt и updatedAt — до микросекунд. Разбор на вашей стороне обязан считать её необязательной: строгий шаблон «только до секунд» упадёт на первом же опросе. Полученное значение передаётся обратно как есть — обрезать его не нужно. В запросах дробная часть не требуется: целые секунды сервер принимает.
Дата без времени (ожидаемый приход поставки) — 2026-08-15. Такая дата понимается как конец указанного дня по московскому времени: документ, выписанный на сегодня, принимается, а опубликованная поставка закрывается в конце этого дня.
Деньги
Целое число копеек, всегда за одну штуку (стебель): 45 ₽ — это 4500, 45 ₽ 50 коп — 4550, 1 200 ₽ — 120000.
Значение передаётся цифрами без кавычек. Дробная часть в этом поле отвергается: 45.5 копейки не существует, и почти всегда это признак того, что передана цена в рублях.
Количества
Целое число штук (не банчей, не коробок).
Кратность банчу обязательна в недостаче и восстановлении: выдать покупателю часть связки платформа не может. При банче 25 допустимы 25, 50, … 250; недопустимы 10, 240, 251. Там же количество строго больше нуля: 0 отвергается кодом 400.
Остатки кратности не требуют — передавайте фактическое число, округлять не нужно: на витрине оно так и встанет, а кратность связке обеспечивает корзина покупателя. Позиции поставки кратности тоже не требуют, но там количество округляется вниз до целых банчей при заведении — фактически применённое значение приходит в ответе.
externalCode — ваш код номенклатуры
Строка до 120 символов, ваш внутренний код или артикул.
Стабильность: код позиции не меняется со временем. Изменившийся код платформа считает новой позицией и заводит рядом вторую — на витрине товар раздвоится. Карточку товара при этом она, как правило, переиспользует: та ищется по совпадению названия, категории, страны, длины стебля и описания.
Уникальность в пределах компании: один код — одна карточка товара. Позиция при этом своя на каждом складе: идентичность позиции — пара «код + склад». Разводить склады суффиксами в коде (RSA-70-MSK01) не нужно и вредно — так одна номенклатура превратится в две карточки.
Регистр значим. Сравнение точное, срезаются только крайние пробелы: rsa-70 и RSA-70 — разные позиции, msk-01 при сопоставленном MSK-01 даст UNKNOWN_WAREHOUSE. Держите регистр кодов стабильным.
То же справедливо для кодов складов и для номера документа поставки.
Цены: передаёте свою — в заказах приходит цена покупателя
Вы → платформа (остатки, поставки): ваша цена продажи за штуку, например 10000 (100 ₽). Платформа → вы (заказы): полная цена покупателя — ваша цена за вычетом скидки покупателя, плюс комиссия платформы, например 10300 (103 ₽).
Цена заказа — та, которую фактически платит покупатель: реализацию и УПД проводите по ценам заказа, а не по прайсу. Порядок арифметики: скидка вычитается из вашей цены, и уже к уменьшенной прибавляется комиссия. Скидка не обязательно персональная — платформа применяет наибольшую из подходящих покупателю (персональная, за объём заказа, за первый заказ).
Комиссия приходит числом: commissionAmount на заказе и на каждой позиции. Ваше нетто берётся вычитанием: amount − commissionAmount по строке, totalAmount − commissionAmount по заказу. Вычислять её самому по ставке НЕ НУЖНО и нельзя: наценка округляется поштучно, обратное деление даёт расхождение, которое копится против вас, ставка со временем меняется, а каждый заказ хранит ту, что действовала в его момент.
Деньги до вас доходят двумя путями. По заказам, оплаченным через платёжный сервис платформы, комиссия удерживается из перечисляемой суммы; по остальным приходит отдельным счётом вне API. В обоих случаях сумма реализации и сумма поступления различаются на величину комиссии — это штатное расхождение, а не ошибка обмена.
Атрибуты карточки: длина стебля может быть взята из названия
Длина стебля — фильтр каталога: без неё товар не попадает ни в фильтр «Длина», ни в разделы, у которых длина задана условием. Поэтому у карточки СРЕЗКИ, заводимой без stemLengthCm, платформа пробует вывести длину из названия: берётся первое отдельно стоящее число из закрытого ряда 30, 35, 40, 45, 50, 55, 60, 65, 70, 80, 90, 100 («Роза Фридом 60» → 60). Число, слипшееся с единицей измерения («60см»), год и любое другое значение выводом не считаются; у остальных категорий вывод не делается вовсе.
Выведенная длина помечена, и поставщик видит в кабинете пометку «из названия» — это подсказка платформы, а не ваши данные.
Переданное значение всегда главнее выведенного ПРИ ЗАВЕДЕНИИ карточки: пришёл stemLengthCm — он и записывается, пометка не ставится. Осознанный ноль остаётся нулём: «поле не передали» и «передали 0» платформа различает.
Атрибуты карточки применяются ТОЛЬКО при её первичном заведении. Позиция с уже известным кодом на карточку не влияет: длина, страна и название, присланные позже, ничего в ней не меняют — карточку ведёт поставщик в личном кабинете. Поэтому длину имеет смысл передавать с первой же выгрузки позиции; исправить её потом можно в кабинете, а обменом — нельзя.
Страна происхождения приводится к принятому на платформе написанию, если совпадает с ним без учёта регистра («эквадор» → «Эквадор»). Незнакомое платформе название сохраняется как прислали — оно уедет в фильтр каталога отдельным значением.
Остаток — физический
Передаётся фактический остаток на складе, без вычета броней покупателей платформы — их вычитает платформа. Двойное вычитание занижает витрину.
Витрина = физический остаток − удержанное платформой. Удерживаются корзинные брони и позиции принятых, но не выданных заказов; оплата роли не играет, выданные заказы не вычитаются, по позиции с недостачей берётся фактическое к выдаче. Ниже нуля результат не опускается.
Схема выгрузки: сначала режим чтения, потом расписание
Режим переключается в кабинете: «Подключённые системы → Системы → Как читать выгрузку остатков». По умолчанию стоит «весь остаток», и в нём каждый пакет — снимок склада целиком: позиция, которой не было в двух выгрузках подряд, считается распроданной и снимается с витрины.
⚠️ Дельта в режиме «весь остаток» не работает, и отдельная полная сверка по расписанию её не спасает: окно платформа считает по вашему же ритму (примерно два соседних цикла, но не меньше 15 минут), а частые дельта-пакеты сами делают это окно коротким — редкая сверка в него не попадает, и позиции уходят с витрины между сверками.
Присылаете снимок остатков — оставьте режим по умолчанию и отправляйте прайс целиком каждой выгрузкой; распроданное убирать нулями не нужно, достаточно перестать присылать его строки.
Присылаете только изменения — сначала переключите режим на «только изменения», и лишь тогда стройте расписание так: отправляйте строку, когда у неё изменился остаток или цена, — сразу или накопленной пачкой раз в несколько минут; и не реже раза в час, круглосуточно, отправляйте весь прайс целиком для сверки. Распроданное в этом режиме снимайте строкой с нулевым quantity: выводов из отсутствия строки платформа здесь не делает, и сверка выправляет только разошедшиеся числа.
Полный прайс каждые десять минут работает, но это в десятки раз больше строк, чем нужно, а при росте числа подключённых систем такие выгрузки приходят на платформу одной минутой. Если прайс большой, это и есть повод перейти на «только изменения» вместе с расписанием выше — а не разрежать снимок: в режиме «весь остаток» редкая выгрузка означает лишь редкое обновление витрины.
⚠️ Прайс длиннее 5 000 строк уходит несколькими пакетами, и одной выгрузкой они считаются, только пока идут подряд: пауза между страницами дольше 5 минут читается как конец одной выгрузки и начало следующей. В режиме «весь остаток» отсюда прямое следствие для витрины — позиции, которые приедут следующими страницами, к этому моменту выглядят пропавшими и после второй такой выгрузки снимаются. Значит, страницы идут вплотную — паузу платформа меряет от окончания обработки одной страницы до окончания обработки следующей, а страница с новыми позициями обрабатывается дольше обычной, — строки одного склада идут подряд, на соседних страницах (склейка страниц идёт по складу, поэтому выгрузку сортируйте по складу), а прерванная выгрузка (пауза по Retry-After, серия 5xx, перезапуск задания) отправляется заново с первой страницы, а не дописывается хвостом.
Первая выгрузка после включения режима «весь остаток» снимает накопившийся хвост позиций, которых на складе давно нет, — у живого поставщика это бывает больше половины ассортимента склада. Отправляйте её полной: если в ней не окажется больше 90 % позиций склада, платформа сочтёт выгрузку сломанной и не снимет ничего. Потолок первого прогона действует и для склада, который выгружается своим пакетом или подключён позже других, если его первый прогон пришёлся на первые двое суток после первого прогона поставщика, а сам склад выгружается не реже раза в 10 часов; иначе по складу работает обычный предохранитель.
Если у регламентного задания фиксированный интервал, сдвиньте его с круглой минуты (10:03, а не 10:00): пакеты разных систем не встанут в одну очередь.
Запись числа
Числовые поля (quantity, pricePerUnit, stemsPerBundle, stemLengthCm, discountBps) принимают целое число цифрами, без кавычек.
Колонка «Итог» в таблице ниже — про запись, а не про годность значения: -3 и 0 записаны верно, но цена и размер банча требуют больше нуля, скидка — диапазона 0–9999, количество недостачи и восстановления — не меньше единицы.
⚠️ Дробная часть не округляется, а отвергается: платформа не берётся угадывать, рубли ей передали или копейки. Так же и с форматированием — «5 000» и «5000,00» не разбираются, потому что «5,000» читается и как пять тысяч, и как пять целых.
Отказ называет поле и присланное значение, а для типовых промахов — прямую причину («передайте число без кавычек», «похоже, передана цена в рублях»).
Отказ по записи числа — построчный в остатках и контрагентах (остальные строки пакета применяются) и 400 на весь вызов в остальных методах; в поставках сообщение называет ещё и номер позиции.
| Пришло | Итог | Почему |
|---|---|---|
| 70, 5000, -3, 0 | ✅ запись принята | целое число |
| "70", "5000" | ❌ VALIDATION_ERROR | число в кавычках — это строка |
| 70.0, 5000.00, 5e2 | ❌ VALIDATION_ERROR | запись не целая, даже если дробная часть нулевая |
| 70.5, 1500.50 | ❌ VALIDATION_ERROR | платформа не округляет |
| "5 000", "5000,00" | ❌ VALIDATION_ERROR | разделители разрядов и десятичная запятая |
| "" | ❌ VALIDATION_ERROR | пустое значение не читается как 0 |
| true, {}, "сто" | ❌ VALIDATION_ERROR | числом не является |
| null | принято как «поле не передано» | значение не подставляется; обязательное поле дальше отвергнут проверки по существу |
Два идентификатора: заказ и позиция
Заказ и каждая его строка имеют собственные UUID: id — идентификатор заказа (orderId), itemId — идентификатор позиции.
В адресе orderId принимают «Готов к выдаче», «Откат готовности» и «Отклонение заказа». В адресе itemId — недостача, восстановление выдачи и удаление позиции.
Путь начинается с /supplier/orders/… в обеих группах — ориентируйтесь на имя параметра в описании метода, а не на слово orders в пути.
Статусы заказа
Жизненный цикл: pre_order (предзаказ) → confirmed (ждёт сборки) → ready (ждёт покупателя) → received (покупатель забрал) → completed (завершён). Терминальные ветки: cancelled — отменён покупателем или платформой, rejected — отклонён вами.
Статус есть у заказа и у каждой позиции отдельно; статус заказа — агрегат по позициям.
pre_order — предзаказ на товар будущей поставки; после прихода поставки сам станет confirmed. received означает, что покупатель получил товар: при самовывозе — скан кода на складе, при доставке — подтверждение покупателем. cancelled ставится и когда заказ аннулирован вместе с отменённой поставкой — в том числе вашей же, отменённой машинно.
⚠️ «Готов к выдаче» означает «собрали», а не «выдали». Метода «отметить выданным» в API нет: received ставит только покупатель — сканом кода на складе при самовывозе либо подтверждением при доставке (и то лишь после кабинетного «Передал в доставку»).
⏱ Самовывозный заказ стоит в ready, пока покупатель не приедет: ждать он может сколь угодно долго. У ДОСТАВКИ с 09.2026 есть таймер: через 12 часов после кабинетного «Передал в доставку» заказ отмечается received автоматически, если покупатель не подтвердил получение сам. Таймер останавливается, если покупатель заявил, что заказ не получен, — тогда заказ снова ждёт человека (поставщик довозит и передаёт заново либо отменяет передачу). Отмена передачи в кабинете тоже гасит таймер, а повторная передача взводит его заново.
⚠️ Если в кабинете выключены рекламации, received может не наблюдаться вовсе: такой заказ переходит сразу в completed в момент выдачи. Опрашивайте статусы по списку, а не ждите received как отдельного шага.
Ваши собственные заказы на платформу не передаются. Продали в офлайне — передайте новый остаток.
Цикл опроса
Проверка изменений запрашивается с since из прошлого ответа. hasChanges = false — до следующего опроса делать нечего; true — лента заказов запрашивается с ТЕМ ЖЕ since, что в первом шаге, и её страницы проходятся по курсору.
⚠️ В запрос ленты передаётся since первого шага, а не только что полученный serverTime, и передаётся обязательно: запрос ленты без since и без cursor отвечает 400.
Новый serverTime становится since следующего опроса и действителен только после того, как все страницы разобраны: на 304 и на любой ошибке момент остаётся прежним.
since — только из serverTime
Успешный ответ проверки изменений содержит serverTime; именно он передаётся как since следующего опроса. Собственные часы не используются: их расхождение с сервером приводит к пропуску изменений.
Сохраняется он не сразу, а после того как все страницы ленты разобраны: пока идёт разбор, since остаётся прежним, иначе сбой на второй странице потерял бы её заказы навсегда.
Два случая, когда двигать since нельзя: ответ 304 (тела нет, брать значение неоткуда) и любая ошибка. В обоих случаях сохраняется прежний момент — повторная выдача уже известных заказов безопасна, а пропуск нового заказа необратим.
Ограничения на глубину окна нет: since может отстоять сколь угодно далеко, лента просто отдаст больше страниц. «Текущее время минус сутки» на первом запуске — рекомендация, а не требование сервера.
Методы
Каждая карточка самодостаточна: адрес, требуемое право, поля, ошибки, грабли и примеры — в одном месте.
Проверка изменений
GET/supplier/integration/changes
Заказы: просмотр20 запросов в минутуподтверждённый профиль не нужен
Дешёвый опрос: были ли изменения по заказам. Единственный метод, который зовут по расписанию. Отвечает «да» — идите за лентой заказов, «нет» — до следующего опроса делать нечего.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Дата и время
Частота запросов
since — только из serverTime
| Поле | Обязательность | Описание |
|---|---|---|
| sincedatetime | обяз. | Момент, с которого искать изменения.
|
| Поле | Приходит | Описание |
|---|---|---|
| hasChangesbool | всегда | true — зовите ленту заказов; false — до следующего опроса делать нечего. |
| ordersChangedint | всегда | Сколько заказов затронуто. |
| serverTimedatetime | всегда | since следующего опроса. |
| nextPollAfterSecondsint | всегда | Интервал до следующего опроса; обязателен к соблюдению.
|
Грабли
serverTime намеренно отстаёт от реального времени примерно на полминуты: это гарантирует, что изменение, попавшее в границу опроса, не потеряется. Плата за это — иногда повторно приезжающий заказ, что лента заказов и так требует переживать.
Подробности
ETag и If-None-Match
| Код | Когда возникает и что делать |
|---|---|
| 400VALIDATION_ERROR | since не передан или не в формате RFC 3339. |
| 429TOO_MANY_REQUESTS | Превышен отдельный потолок опроса — 20 запросов в минуту.Соблюдайте nextPollAfterSeconds из прошлого ответа. |
Лента заказов
GET/supplier/integration/orders
Заказы: просмотрподтверждённый профиль не нужен
Заказы, изменившиеся после since, в порядке изменения.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Дата и время
Два идентификатора: заказ и позиция
Деньги
Пагинация
Цены: передаёте свою — в заказах приходит цена покупателя
since — только из serverTime
Статусы заказа
Идентификаторы
| Поле | Обязательность | Описание |
|---|---|---|
| sincedatetime | условно | Тот же момент, что в проверке изменений.Обязательно: Для первой страницы.
|
| cursorstring | условно | meta.cursor предыдущей страницы.Обязательно: Для последующих страниц.
|
| limitint | опцион. | 1–100, по умолчанию 50.
|
| Поле | Приходит | Описание |
|---|---|---|
| idUUID | всегда | Идентификатор заказа (orderId). |
| numberstring | всегда | Человекочитаемый номер заказа — 50250. У заказов, созданных до перехода на короткие номера, он выглядит иначе (00050249-0001): старые номера не переименовывались, форматы сосуществуют. В API-запросах номер не используется — адресация везде по id, поэтому разбирать его не нужно. |
| statusstring | всегда | Статус заказа. |
| createdAtdatetime | всегда | Создан. |
| updatedAtdatetime | всегда | Последнее изменение; по нему работает since. |
| warehouseobject | всегда | Склад выдачи: code — ваш код по сопоставлению (null, если не сопоставлен), name, address. Если точку выдачи удалили на платформе, у её заказов code остаётся прежним, пока код не перенесли на другую точку или не убрали из сопоставления, — хотя остатки и поставки с этим кодом уже отклоняются. |
| buyerobject | всегда | Покупатель: legalName, inn, managerEmail. Пустой inn возможен и означает «реквизитов сейчас нет»: у аккаунта, удалённого и обезличенного по 152-ФЗ (его терминальные заказы остаются в ленте) — тогда legalName приходит заглушкой «Удалённый аккаунт»; либо у живого покупателя, которому Оператор сбросил ИНН до повторной верификации. Заводить контрагента по пустому inn не нужно ни в том, ни в другом случае — во втором реквизиты вернутся, и заказ приедет с ними в следующей выборке.
|
| notestring | null | всегда | Комментарий покупателя. |
| totalAmountint | всегда | Сумма заказа к оплате покупателем, копейки.
|
| commissionAmountint | всегда | Вознаграждение платформы, вшитое в totalAmount, копейки.
|
| paymentobject | всегда | Оплата: method — direct (покупатель рассчитывается с вами напрямую) или platform (через платёжный сервис платформы). При platform после перечисления денег добавляются paidOutAt и paidOutAmount (фактически перечислено вам, копейки — после комиссии и недостач). |
| itemsarray<Позиция> | всегда | Позиции заказа. |
| itemIdUUID | всегда | Идентификатор позиции (itemId). |
| externalCodestring | null | всегда | Ваш код номенклатуры. null = позиция заведена в кабинете вручную и с вашим учётом не связана. |
| titlestring | всегда | Название сорта. |
| quantityint | всегда | Заказано, штук. |
| fulfilledQuantityint | null | всегда | Фактически к выдаче после недостачи; null = равно quantity. |
| stemsPerBundleint | всегда | Размер банча. Всегда положительное число, 0 и null не приходят — на него можно делить без проверок. |
| pricePerUnitint | всегда | Цена покупателя за штуку, копейки. |
| amountint | всегда | Сумма строки в ценах покупателя, копейки: pricePerUnit × quantity, то есть снимок изначально заказанного.
|
| commissionAmountint | всегда | Вознаграждение платформы, вшитое в amount, копейки.
|
| statusstring | всегда | Статус позиции. |
| supplyExternalCodestring | null | всегда | У pre_order-позиций: номер вашего документа поставки. null приходит и тогда, когда поставку завёл человек в кабинете или другая учётная система — такие предзаказы связывайте со своими документами по itemId, а не по номеру. |
| supplyExpectedAtdate | null | всегда | У pre_order-позиций: ожидаемая дата прихода поставки. Календарная дата в часовом поясе платформы (по умолчанию московский), не UTC, — ровно та, что вы передали при создании поставки. |
Массив заказов в data плюс meta с курсором страницы.
Что гарантирует метод
- Это лента изменений, не только новых заказов: заказ приходит повторно при отмене, недостаче, выдаче, а также при смене статуса одной его позиции — например, когда кладовщик отметил готовой отдельную строку или откатил готовность частично собранного заказа. Каждый пришедший заказ — его текущее состояние целиком.
- Терминальные заказы (cancelled, rejected, completed) тоже приходят.
- Заказ может прийти повторно и без видимых изменений.
- Замена позиции. Если товара нет, поставщик вправе предложить покупателю замену; при согласии исходная строка приходит со статусом cancelled, а в заказе появляется новая строка с новым itemId, своим externalCode, названием и ценой. Сумма заказа пересчитывается платформой. Через API замена не предлагается — это действие человека в кабинете.
- Заказ приходит повторно и при перечислении вам денег: в payment появляются paidOutAt и paidOutAmount.
- Заказ поднимается в ленту и при приходе поставки — предзаказные позиции становятся обычными, даже если статус самого заказа не изменился.
- Доставочный заказ поднимается в ленту и когда поставщик отмечает в кабинете передачу в доставку (или отменяет её). Видимых отличий в проекции заказа при этом нет: способ выдачи и отметка передачи в неё не входят — заказ придёт тем же составом и в том же статусе ready.
Грабли
cursor присутствует в meta, только если на странице есть хотя бы один заказ. На пустой странице поля нет вовсе — не пустая строка и не null: строгий разбор, ожидающий его всегда, споткнётся.
Грабли
Равенство «Σ amount = totalAmount» в сверку закладывать нельзя. Расходятся в обе стороны. Доставка входит в totalAmount, но строкой не приходит — сумма заказа больше. Недостача (а при прямом расчёте и одобренная рекламация) уменьшает totalAmount, тогда как amount остаётся снимком заказанного — сумма строк больше. Реализацию проводите по фактическим величинам строк: pricePerUnit × (fulfilledQuantity ?? quantity) по неотменённым. Вывести из этой разницы сумму доставки нельзя: после первой рекламации в ней сидит ещё и возврат.
Подробности
Чего в ленте нет
| Код | Когда возникает и что делать |
|---|---|
| 400VALIDATION_ERROR | Запрос без since и без cursor либо since в неверном формате. |
| 400BAD_REQUEST | Нечитаемый курсор.Начать ленту заново с since. |
Готов к выдаче
POST/supplier/orders/{orderId}/ready
Заказы + действие «Выдача заказов»
Заказ собран. Переводит в ready позиции со статусом confirmed, покупатель получает уведомление.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Два идентификатора: заказ и позиция
Статусы заказа
| Поле | Обязательность | Описание |
|---|---|---|
| orderIduuid | обяз. | Идентификатор заказа. |
Тела нет.
| Поле | Приходит | Описание |
|---|---|---|
| statusstring | всегда | Статус заказа после вызова: ready. |
Что гарантирует метод
- Повторный вызов безопасен: если готовить нечего (заказ готов, выдан, отклонён), платформа отвечает 200 и ничего не меняет.
- Успех означает «мы вас поняли», а не «заказ теперь готов»: по отменённому, отклонённому, уже выданному или завершённому заказу метод тоже отвечает 200, просто ничего не меняя. Считать заказ собранным только на основании кода ответа нельзя.
Грабли
Позиции-предзаказы метод не трогает. Заказ, состоящий только из них, ответит 200, но ничего не изменится. В смешанном заказе метод переведёт обычные позиции в ready, и покупатель получит уведомление о готовности, но у САМОВЫВОЗНОГО заказа код выдачи выпускается, только когда готовы ВСЕ активные позиции: пока предзаказная позиция ждёт прихода поставки, забрать заказ нельзя. У доставочного заказа кода выдачи не бывает вовсе — его получение подтверждает покупатель в приложении после того, как поставщик отметит передачу в доставку.
Подробности
Заголовок типа содержимого не проверяется
| Код | Когда возникает и что делать |
|---|---|
| 400BAD_REQUEST | В адресе не UUID.Проверьте, что подставлен orderId. |
| 404NOT_FOUND | Нет такого заказа, или в нём нет ваших позиций. |
| 409CONFLICT | Заказ ожидает оплаты через платёжный сервис платформы — отметка о готовности недоступна. |
Недостача по позиции
POST/supplier/orders/{itemId}/shortfall
Заказы + действие «Отклонение и недостачи»
По одной строке не удаётся выдать всё количество.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Количества
Запись числа
Два идентификатора: заказ и позиция
| Поле | Обязательность | Описание |
|---|---|---|
| itemIduuid | обяз. | Идентификатор позиции. |
| Поле | Обязательность | Описание |
|---|---|---|
| quantityint | обяз. | Размер недостачи — сколько штук НЕ будет выдано (не новое количество к выдаче).
|
| reasonstring | обяз. | Причина. Сохраняется в журнале заказа; покупателю текст не показывается. |
| restockbool | опцион. | Вернуть ли недоданное в остаток витрины. Для интегрированных систем — false: остаток установит ваша следующая передача остатков и цен.
|
Ответ 200 — { "data": { "message": "..." } }. Актуальное состояние строки придёт ближайшей лентой заказов, отдельно запрашивать не нужно.
Грабли
quantity — размер недостачи, а не остаток к выдаче. Передаётся, сколько штук НЕ будет выдано. Пример: заказано 250 шт (10 банчей по 25), не хватает трёх банчей → quantity: 75, к выдаче останется 175.
Грабли
Недостача на всё количество закрывает позицию. Заказ ведёт себя так же, как после удаления позиции: последняя позиция уводит его в rejected. Но статус самой строки различается — после полной недостачи она приходит в ленте как cancelled, после удаления позиции как rejected. Деньги за недоданное возвращаются покупателю автоматически.
Подробности
У предзаказной позиции restock не читается вовсе
| Код | Когда возникает и что делать |
|---|---|
| 400BAD_REQUEST | Тело не разобралось (тело обязательно) либо в адресе не UUID. |
| 400VALIDATION_ERROR | Некратно банчу, ≤ 0, больше текущего к выдаче, не передана причина либо quantity не является целым числом. |
| 404NOT_FOUND | Нет такой позиции.Проверить, что передан itemId, а не id заказа. |
| 409CONFLICT | Позиция не активна (выдана/отменена). |
Восстановление после ошибочной недостачи
POST/supplier/orders/{itemId}/restore
Заказы + действие «Отклонение и недостачи»
Возврат в выдачу единиц, снятых ошибочной недостачей: quantity — прибавка сверх текущего к выдаче, зеркало недостачи по позиции.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Количества
Запись числа
Два идентификатора: заказ и позиция
| Поле | Обязательность | Описание |
|---|---|---|
| itemIduuid | обяз. | Идентификатор позиции заказа. |
| Поле | Обязательность | Описание |
|---|---|---|
| quantityint | обяз. | Прибавка, то есть сколько штук вернуть в выдачу сверх текущего (не итоговое количество к выдаче — зеркало недостачи по позиции).
|
| reasonstring | опцион. | Причина; здесь не обязателен (в отличие от недостачи по позиции и удаления позиции), но попадает в журнал заказа. |
Ответ 200: { "data": { "message": "..." } }
Грабли
Недостача на всю строку необратима. Позиция после полной недостачи становится отменённой, и восстановление отвечает 409 при любом наличии товара: статус проверяется раньше остатка. Замена позиции по согласованию с покупателем оформляется только человеком в кабинете.
Подробности
Пример
Восстановление берёт единицы из текущего свободного объёма партии, даже если недостача объявлялась с restock: false. Поэтому успех не гарантирован: между недостачей и восстановлением товар мог уйти другим покупателям.
| Код | Когда возникает и что делать |
|---|---|
| 400BAD_REQUEST | Тело не разобралось (тело обязательно) либо в адресе не UUID. |
| 400VALIDATION_ERROR | Некратно банчу, ≤ 0, итог выше исходно заказанного либо quantity не является целым числом. |
| 404NOT_FOUND | Нет такой позиции. |
| 409CONFLICT | Позиция уже выдаётся в полном объёме, не активна, либо возвращать не из чего: у позиции в наличии не хватает свободного остатка (товар разобрали другие покупатели), у предзаказной — свободного объёма поставки. |
Удаление позиции
POST/supplier/orders/{itemId}/remove
Заказы + действие «Отклонение и недостачи»
Строка не будет выдана вовсе.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Два идентификатора: заказ и позиция
Статусы заказа
| Поле | Обязательность | Описание |
|---|---|---|
| itemIduuid | обяз. | Идентификатор позиции заказа, а не заказа. |
| Поле | Обязательность | Описание |
|---|---|---|
| reasonstring | обяз. | Причина. Сохраняется в журнале заказа; покупателю текст не показывается. |
| restockbool | опцион. | Вернуть ли недоданное в остаток витрины.
|
Успешный ответ несёт единственное поле message.
Что гарантирует метод
- Деньги за позицию возвращаются.
- Удаление последней позиции переводит заказ в rejected.
- Убрать позицию можно и из уже собранного заказа (ready).
Подробности
У предзаказной позиции restock не читается вовсе
| Код | Когда возникает и что делать |
|---|---|
| 400VALIDATION_ERROR | Не передана причина. |
| 400BAD_REQUEST | Тело не разобралось. |
| 404NOT_FOUND | Нет такой позиции. |
| 409CONFLICT | Позиция уже выдана или не активна. |
Отклонение заказа
POST/supplier/orders/{orderId}/reject
Заказы + действие «Отклонение и недостачи»
Когда заказ невозможно выдать целиком.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Два идентификатора: заказ и позиция
Статусы заказа
| Поле | Обязательность | Описание |
|---|---|---|
| orderIduuid | обяз. | Идентификатор заказа — поле id, а не itemId позиции. |
| Поле | Обязательность | Описание |
|---|---|---|
| reasonstring | условно | Причина отклонения.Обязательно: Обязательна, если в заказе есть собранные (ready) позиции — иначе 400 VALIDATION_ERROR.
|
{ "data": { "message": "Order rejected" } }
Что гарантирует метод
- Все позиции закрываются, деньги возвращаются, заказ — терминальный rejected.
- Если проблема в одной строке — недостача по позиции или удаление позиции.
Грабли
Повтор на уже отклонённом заказе — 409, а не тихий успех (в отличие от «Готов к выдаче» и «Отката готовности»).
| Код | Когда возникает и что делать |
|---|---|
| 400BAD_REQUEST | Присланное тело не разобралось. |
| 400VALIDATION_ERROR | Не передана причина, а в заказе есть собранные (ready) позиции. |
| 404NOT_FOUND | Нет такого заказа или в нём нет ваших позиций. |
| 409CONFLICT | Заказ уже отменён либо отклонён, активных позиций не осталось или он ушёл дальше выдачи. |
Откат готовности
POST/supplier/orders/{orderId}/unready
Заказы + действие «Выдача заказов»
Документ сборки распроведён или удалён — заказ ещё не собран.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Два идентификатора: заказ и позиция
Статусы заказа
| Поле | Обязательность | Описание |
|---|---|---|
| orderIduuid | обяз. | Идентификатор заказа. |
| Поле | Обязательность | Описание |
|---|---|---|
| reasonstring | опцион. | Причина отката.
|
| Поле | Приходит | Описание |
|---|---|---|
| statusstring | всегда | Статус заказа после вызова: confirmed. |
Что гарантирует метод
- Возвращает заказ из «готов к выдаче» в «ждёт сборки» и снимает отметку о передаче в доставку, если она была.
- Покупатель не получает уведомления об отмене — только о повторной готовности, когда вы снова отправите ready.
- Повторный вызов безопасен: заказ, уже ожидающий сборки, отвечает 200 без изменений.
Грабли
Испорченное тело не отвергается. Откат готовности читает тело мягко — тело можно не передавать, а испорченное тело там не отвергается: вызов проходит (200), и reason берётся по умолчанию («Сборка отменена в учётной системе»). Так сделано намеренно: откат готовности меняет состояние заказа, и потерять сам откат из-за формы необязательного поля дороже, чем потерять текст причины.
Подробности
Заголовок типа содержимого не проверяется
| Код | Когда возникает и что делать |
|---|---|
| 404NOT_FOUND | Нет такого заказа или в нём нет ваших позиций. |
| 409CONFLICT | Заказ уже выдан покупателю, терминален либо активных позиций не осталось. |
Передача остатков и цен
POST/supplier/integration/stock
Товары и партии: полныйдо 5 000 строк в пакетене больше 150 новых позиций за запроспо умолчанию — весь остаток складагалочки действий проверяются построчно
Пакет строк «код + склад → сколько и почём». Что означает НЕ пришедшая строка, задаёт режим чтения выгрузки в кабинете: по умолчанию платформа читает пакет как весь остаток склада и считает пропавшую позицию распроданной. Слать только изменившиеся строки можно лишь в режиме «только изменения».
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Запись числа
Деньги
Количества
externalCode — ваш код номенклатуры
Остаток — физический
Атрибуты карточки: длина стебля может быть взята из названия
Схема выгрузки: сначала режим чтения, потом расписание
Цены: передаёте свою — в заказах приходит цена покупателя
Ограничение размещения за неоплаченный счёт
Режим завершения дел
| Поле | Обязательность | Описание |
|---|---|---|
| linesarray<Строка остатка> | обяз. | Массив строк остатка. Строка — это «сколько сейчас лежит на складе и почём»: один ваш код номенклатуры на одном складе. Никакого другого источника этих строк нет — вы формируете их из своего регистра остатков.
|
| externalCodestring | обяз. | Код номенклатуры.
|
| warehouseCodestring | обяз. | Код склада из сопоставления в кабинете.
|
| quantityint | обяз. | Физический остаток, штук; кратность не требуется.
|
| pricePerUnitint | обяз. | Цена за штуку, копейки.
|
| stemsPerBundleint | обяз. | Размер банча.
|
| titlestring | условно | Название сорта — из него создаётся карточка.Обязательно: пока платформа не знает карточку товара под этим кодом
|
| categorystring | условно | Категория.Обязательно: пока платформа не знает карточку товара под этим кодом
|
| countryOfOriginstring | опцион. | Страна происхождения — фильтр каталога. Не обязательна, но желательна.
|
| stemLengthCmint | опцион. | Длина стебля, см — фильтр каталога. Не обязательна, но желательна.
|
| plantationstring | опцион. | Плантация.
|
| Поле | Приходит | Описание |
|---|---|---|
| acceptedint | всегда | Сколько строк пакета применено. |
| rejectedint | всегда | Сколько строк пакета отклонено. |
| resultsarray<Отказ по строке> | всегда | Только ошибочные строки.
|
| indexint | всегда | Номер строки в вашем запросе, с нуля. |
| externalCodestring | всегда | Код номенклатуры из отклонённой строки. |
| statusstring | всегда | Всегда error: results содержит только ошибочные строки. |
| codestring | всегда | Построчный код отказа. |
| messagestring | всегда | Пояснение отказа человеческим языком. |
Что гарантирует метод
- Строка обрабатывается в перечисленном ниже порядке, и на неё возвращается только первый сработавший код отказа: пакет с несколькими проблемами в одной строке чинится в несколько заходов.
- Формат и диапазоны полей строки.
- Сопоставление warehouseCode с вашим складом; нет сопоставления или точка выдачи удалена — UNKNOWN_WAREHOUSE.
- Поиск позиции в наличии по паре externalCode + warehouseCode.
- Позиция найдена → применяются остаток и цена. Найдено несколько — AMBIGUOUS_EXTERNAL_CODE.
- Позиция не найдена → проверяется право «Внесение товара» (PERMISSION_DENIED), затем потолок новых позиций за запрос (NEW_POSITIONS_LIMIT), затем заводятся карточка и позиция (title и category нужны, только если платформа ещё не знает карточку под этим кодом).
Грабли
title и category нужны при первом появлении КОДА. Они требуются, только когда платформа ещё не знает карточку под этим кодом. Если карточка уже есть — выгрузка по второй точке, возврат кода после удаления позиции, код, живший до сих пор только в поставке, — новая позиция заводится и без них. У заведённой позиции эти поля игнорируются, так что слать их можно в каждой строке.
Грабли
У заведённой позиции выгрузка меняет только остаток и цену. Название, категория, размер банча, страна, длина стебля и плантация применяются исключительно при первичном заведении — дальше они правятся только в кабинете. Сменить банч выгрузкой нельзя.
Грабли
Галочки действий проверяются построчно, а не на весь вызов. Новый код на складе требует «Внесение товара», изменение цены существующей позиции — «Изменение цен», остаток 0 и меньше по заведённой позиции — «Списание остатков». Не хватило галочки — построчный PERMISSION_DENIED получает только эта строка, остальные применяются. Отказ по цене отменяет строку целиком, вместе с остатком.
Грабли
Строка, переставшая приходить. По умолчанию платформа считает, что вы присылаете весь остаток: позиция, которой не было в двух выгрузках подряд, считается распроданной и снимается с витрины (остаток 0, брони и заказы по ней остаются; если по ней есть невыданные заказы, поставщик получает плашку расхождения остатка — ту же, что при явном нуле). Ритм выгрузок платформа определяет сама — вывод начинает работать после трёх выгрузок. Выгрузки реже, чем раз в двое суток, под правило не попадают вовсе: историю присутствия платформа хранит 48 часов, и двух выгрузок подряд в ней не набирается — вкладка «Здоровье» говорит об этом отдельной строкой, а распроданное в таком ритме снимайте строкой с quantity: 0. Склад, по которому за двое суток не набирается трёх выгрузок (например, выгружаемый раз в сутки), платформа снимает по отсутствию только по общему ритму ваших выгрузок — если и остальные склады выгружаются так же редко. Рядом со складами, которые выгружаются часто, распроданное на таком складе не снимается совсем, и вкладка «Здоровье» об этом отдельно не предупреждает: обнуляйте его строкой с quantity: 0 или выгружайте склад чаще. Не попадают под правило и выгрузки склада, идущие без пауз дольше 5 минут: такие пакеты платформа склеивает в одну выгрузку, и непрерывный поток по складу для неё — одна бесконечная выгрузка; вкладка «Здоровье» называет и это, а распроданное при таком ритме тоже снимайте строкой с quantity: 0. Ритм и склейка считаются по каждому складу отдельно: склады, которые вы выгружаете разными пакетами вразбежку (один склад в 10:00, другой в 10:03), друг другу не мешают — если каждый из них выгружается хотя бы трижды за двое суток. Если ваша система присылает только изменения, переключите режим в кабинете («Подключённые системы → Системы → Как читать выгрузку остатков») и снимайте распроданное строкой с quantity: 0. Пауза выгрузок ничего не снимает: вывод делается только пока выгрузки продолжают приходить. Снятие по отсутствию — то же списание, поэтому у ключа должна быть галочка «Списание остатков»; без неё платформа ничего не снимает и говорит об этом во вкладке «Здоровье». Вывод делается по каждому складу отдельно и только по позициям, за которыми закреплён ваш код: позиция без сопоставленного кода и позиция будущей поставки под правило не попадают.
Грабли
Прайс, который не помещается в один пакет. Прайс длиннее 5 000 строк отправляйте страницами подряд, с паузой не больше 5 минут и с запасом: паузу платформа меряет от окончания обработки одной страницы до окончания обработки следующей, а страница с новыми позициями обрабатывается дольше обычной. Более длинную паузу платформа читает как конец одной выгрузки и начало следующей, а в режиме «весь остаток» из выгрузок делается вывод о пропавших позициях: то, что приедет следующими страницами, к этому моменту уже выглядит пропавшим, и после второй такой выгрузки снимается с витрины. Опасен ровно один случай — три и более страницы с паузами дольше 5 минут: прайс, разложенный на четыре пакета с паузой в шесть минут, каждый цикл теряет с витрины около половины позиций, и возвращают их следующие страницы через несколько минут. Две страницы безопасны при любой паузе, а пауза заметно короче 5 минут — при любом числе страниц, если строки одного склада идут подряд, на соседних страницах. Страницы платформа склеивает по складу, и у склада, строки которого разбросаны по всему прайсу (например, только на страницах 1, 8 и 15), между его страницами паузы дольше 5 минут. Пока весь прайс уходит меньше чем за час, такой склад прикрывает общий ритм ваших выгрузок; но выгрузка, растянутая на час и дольше, — как и прайс на фоне других выгрузок остатков, идущих часами без пауз дольше 5 минут (например, складов вразбежку), — общего ритма не имеет, и тогда каждая страница разбросанного склада снимает часть его позиций, а следующая их возвращает, в каждом цикле. Поэтому выгрузку сортируйте по складу. На предохранитель тут рассчитывать нельзя: он считает долю склада, и ровно половина ассортимента его не останавливает. Прерванную выгрузку — пауза по Retry-After, серия 5xx, перезапуск задания — отправляйте заново с первой страницы, а не дописывайте хвостом. И учтите, что присутствие ведётся по паре «поставщик и склад», ключ доступа в нём не участвует: два задания или два экземпляра вашей системы, делящие один склад между собой, читаются как страницы одной выгрузки и по тем же правилам снимают позиции друг друга.
Грабли
Первый прогон снимает накопившийся хвост разом. Пока вывод об отсутствии не работал, на витрине копились позиции, которых на складе давно нет; у живого поставщика это бывает больше половины ассортимента склада. Первый же полноценный прогон уберёт их все за один цикл — это ожидаемо, и обычный предохранитель (см. ниже) на нём не применяется. Свой потолок у первого прогона всё же есть: если в выгрузке не оказалось больше 90 % позиций склада, снятие пропускается целиком — такой пакет платформа считает сломанной выгрузкой, а не распродажей. Поэтому первую выгрузку после включения режима «весь остаток» отправляйте полной и предупредите поставщика, что витрина в этот день заметно похудеет. Первый прогон считается для каждого склада отдельно: склад, выгружаемый своим пакетом или подключённый позже других, получает свой первый прогон с тем же потолком, если он пришёлся на первые двое суток после первого прогона по любому складу поставщика. Позже, а также у склада, который выгружается реже раза в 10 часов, по складу действует обычный предохранитель: иначе платформа ослабляла бы его после каждой паузы обмена дольше двух суток. Чтобы снять хвост склада, подключённого позже, поставщик ещё раз выбирает в кабинете «Присылаете весь остаток» — выбор возвращает первый прогон всем складам, — и следующую выгрузку вы отправляете полной.
Грабли
Отклонённая строка отсутствием не считается. Строка, которую платформа прочитала и отклонила построчно, позицию не снимает: код в ней назван — значит позиция в вашей системе есть, сколько бы выгрузок подряд эта строка ни отклонялась. Исключений три. Первые два — строки, код которых привязать не к чему: строка с несопоставленным складом (UNKNOWN_WAREHOUSE) и строка, которую не удалось разобрать вовсе (число в кавычках, битый тип поля); такие до правила не доезжают, и их позиции выглядят пропавшими. Третье — пакет, в котором по этому складу не применилось НИ ОДНОЙ строки: выгрузкой по складу он не считается (пакет, из которого не применилось ничего, говорит о сломанной обвязке, а не о наличии), поэтому коды его отклонённых строк не сохраняются вовсе. Сам по себе такой пакет ничего не снимает — по складу, названному только им, шаг не выполняется; но если тот же склад в том же цикле назван другим, удачным пакетом, позиции из отклонённого целиком пакета выглядят пропавшими. Поэтому несопоставленный склад, неразбираемые строки и пакет, отклонённый целиком, в режиме «весь остаток» чинятся сразу, а не «когда дойдут руки».
Грабли
Нулевая строка по НОВОМУ коду заводит позицию. Карточка и позиция создаются всё равно, просто с нулевым остатком: выгрузка всего прайса нулями наполнит каталог пустыми позициями. Снимать с витрины нулём имеет смысл только уже заведённые позиции. В режиме «весь остаток» нули по заведённым позициям слать не обязательно — достаточно перестать присылать их строки.
Грабли
Один код не ведёт одновременно позицию в наличии и позицию поставки на одном складе. До прихода это разные сущности, но при приходе позиция поставки встаёт в наличие с тем же кодом — и каждая строка stock по нему начинает отвечать AMBIGUOUS_EXTERNAL_CODE, пока позиции не сведены в кабинете. На разных складах они живут мирно: идентичность позиции — пара «код + склад».
Грабли
Новый код заводит позицию СКРЫТОЙ — до одобрения поставщиком. По умолчанию новая карточка попадает в очередь кабинета («Подключённые системы → Новые товары»): поставщик проверяет подобранную фотографию, название и категорию и выпускает товар на витрину сам. Метод при этом отвечает 200, остаток и цена применяются, следующие выгрузки продолжают обновлять числа — покупателям товар просто не виден до решения человека. Одобрения не требуют позиции, уже видимые на витрине; код, чей товар скрыт (в том числе отклонённый из очереди ранее), при заведении НОВОЙ партии — например, на втором складе — снова встаёт в очередь. Правило снимается тумблером «Автоматическая связка» в кабинете; по умолчанию он выключен.
Подробности
Удаление позиции на платформе — локальная операция
Предохранитель: платформа не снимает пол-склада разом
Если вы строите выгрузку в 1С
Разбор примера тела
Итог построчный, запись — пакетом
Большой новый прайс заводится за несколько выгрузок
Позиции, заведённые на платформе до подключения
Фотографии выгрузкой не передаются
Где ещё видно позиции, ждущие проверки
Позиция принята, а покупатели её не видят
| Код | Когда возникает и что делать |
|---|---|
| 400BAD_REQUEST | Тело не разобралось как JSON целиком либо lines пришло не массивом. |
| 400VALIDATION_ERROR | Больше 5 000 строк; тело без массива lines; пустой пакет — только в режиме «только изменения».Запрос отвергается целиком, построчного results в этом случае нет; прайс длиннее отправляется несколькими запросами. Тело без массива lines — это ошибка обвязки (опечатка в имени поля, рассинхрон версий), а не «склад пуст»: платформа отвечает 400 и остатков не трогает. В режиме «весь остаток» ЯВНЫЙ пустой пакет (lines: []) принимается и означает «в наличии ничего» на складах, куда выгрузка приходила последние двое суток; правило двух выгрузок подряд действует и здесь — одиночный пустой пакет ничего не снимает. |
| 403BILLING_RESTRICTED | Поставщику приостановлено размещение нового: отклоняется весь пакет целиком, results не приходит.Ответ разбирается до data, по HTTP-коду. Не повторять; после снятия ограничения выгрузить полную сверку прайса. |
| 403ACCOUNT_WIND_DOWN | Владелец отказался от условий платформы — аккаунт доводит начатое и нового не заводит. Пакет отклоняется целиком, results не приходит.Не повторять: режим снимает только владелец, приняв условия. Сообщите поставщику; после возврата выгрузить полную сверку прайса. |
| Код | Когда возникает и что делать |
|---|---|
| UNKNOWN_WAREHOUSE | Код склада не сопоставлен в кабинете или сопоставлен с точкой выдачи, которую удалили на платформе.Сопоставьте код склада в кабинете («Подключённые системы → Склады»); код удалённой точки перенесите там на другую точку. В режиме «весь остаток» с этим не тянут: код такой строки платформе не к чему привязать, он не попадает в присутствие, и позиция под ним может уйти с витрины как пропавшая — если её склад в том же пакете назван другими строками. |
| AMBIGUOUS_EXTERNAL_CODE | Под кодом на этом складе найдено несколько позиций — платформа не выбирает за вас, сведите их в кабинете. |
| PERMISSION_DENIED | У ключа снята галочка действия, которого требует строка: «Внесение товара» (новая позиция), «Изменение цен» (цена отличается от текущей), «Списание остатков» (quantity ≤ 0 по уже заведённой позиции). Остальные строки пакета применяются.Лечится в карточке ключа. |
| NEW_POSITIONS_LIMIT | Строка заводит новую позицию сверх потолка запроса: один запрос заводит не больше 150 новых позиций — пар «код + склад», под которыми ещё нет позиции в наличии, — в порядке строк пакета. Второе появление того же нового кода на том же складе в счёт не идёт: уложилось в потолок первое — применяется и второе, отложено первое — отложено и второе. Не идут в счёт и строки, которым платформа отвечает без попытки заведения: с ошибкой в своих данных и с кодом, по которому ждёт решения предложение «похоже на вашу позицию», пока строка по-прежнему похожа на позицию из предложения. Отложенная строка в этом запросе не применяется; на строки по уже заведённым позициям потолок не действует.Единственный построчный отказ, после которого ту же строку присылают снова без исправлений. В режиме «весь остаток» делать ничего не нужно: следующая выгрузка несёт ту же строку и заведёт её, когда до неё дойдёт очередь. В режиме «только изменения» пришлите отложенные строки ещё раз — следующим запросом или вместе со следующей пачкой изменений. |
| VALIDATION_ERROR | Формат, диапазон, отсутствует обязательное поле — а также отказы по существу: «позиция больше не в наличии», «код закреплён за другим вашим товаром», «код похож на вашу позицию …». |
Создание поставки
POST/supplier/integration/supplies
Поставки: полный + действие «Внесение товара»до 500 позиций в документе
Проведён заказ поставщику — известны состав и дата прихода. Поставка появляется на витрине заранее, покупатели оформляют предзаказы.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Запись числа
Деньги
Количества
externalCode — ваш код номенклатуры
Атрибуты карточки: длина стебля может быть взята из названия
Дата и время
Ограничение размещения за неоплаченный счёт
Режим завершения дел
| Поле | Обязательность | Описание |
|---|---|---|
| externalCodestring | обяз. | Номер вашего документа; он же защита от дублей.
|
| warehouseCodestring | обяз. | Склад прихода.
|
| expectedAtdate | обяз. | Ожидаемая дата прихода — конец указанного дня.
|
| publishAtdatetime | опцион. | Когда показать покупателям. Не указан → черновик.
|
| itemsarray<Позиция> | обяз. | Позиции документа — строки того же вида, что строка остатка, но без warehouseCode: склад один на всю поставку и задан в шапке.
|
| externalCodestring | обяз. | Код номенклатуры.
|
| quantityint | обяз. | Не остаток, а сколько штук вы ждёте.
|
| pricePerUnitint | обяз. | Цена за штуку, копейки; строго больше нуля, до 10 000 000 (100 000 ₽). |
| stemsPerBundleint | обяз. | Размер банча, до 10 000. Обязателен в каждой строке.
|
| titlestring | условно | Название сорта — из него создаётся карточка. До 255 символов.Обязательно: Пока под кодом нет карточки товара вовсе. |
| categorystring | условно | Категория: cut_flowers — срезанные цветы, plants — растения, dried — сухоцветы, artificial — искусственные цветы, supplies — флористические материалы.Обязательно: Пока под кодом нет карточки товара вовсе.
|
| countryOfOriginstring | опцион. | Страна происхождения — фильтр каталога. До 255 символов.
|
| stemLengthCmint | опцион. | Длина стебля, см — фильтр каталога. Диапазон 0–1000.
|
| plantationstring | опцион. | Плантация. До 255 символов. |
| Поле | Приходит | Описание |
|---|---|---|
| iduuid | всегда | Платформенный идентификатор поставки. Хранить не нужно — адресуйтесь своим externalCode. |
| externalCodestring | всегда | Номер вашего документа. |
| statusstring | всегда | draft — черновик, покупателям не виден; scheduled — момент показа назначен, но ещё не наступил; active — показана покупателям; closed — закрыта (приход состоялся); cancelled — отменена. |
| warehouseCodestring | всегда | Ваш код склада по сопоставлению. Пустая строка, если склад поставки ни с одним вашим кодом не сопоставлен. |
| expectedAtdate | всегда | Дата прихода — календарная дата в часовом поясе платформы. |
| publishAtdatetime | условно | Момент показа покупателям.Когда приходит: У черновика без публикации поле отсутствует. |
| itemsarray<Позиция> | всегда | Позиции: externalCode, title, quantity (после округления вниз до целых банчей), pricePerUnit, stemsPerBundle, reservedQuantity.
|
Ответ 201 — созданная поставка. Позиции приходят с фактически применённым количеством: оно округляется вниз до целых банчей, поэтому сверяйте его с отправленным — остаток от округления в поставку не попадает.
Что гарантирует метод
- Один ваш документ = один вызов со всеми позициями.
- Повторный вызов с тем же externalCode возвращает существующую поставку с ответом 200, но только если у запроса новый Idempotency-Key: повтор со старым ключом обслуживает идемпотентность и отдаёт сохранённый ответ первой попытки — с исходным 201. Ориентируйтесь на тело ответа (status, состав items), а не на различие 200/201.
- Идемпотентность по номеру документа наступает после разбора тела: повтор с телом, которое успело устареть (наступила дата прихода, склад рассопоставлен), отвечает 400, хотя поставка уже создана и живёт на витрине. Отдельного метода чтения поставки нет, а изменение поставки разбирает тело теми же правилами — с той же устаревшей датой оно ответит тем же 400. Устраните причину и вызовите изменение поставки полным документом: пришлите новую будущую дату прихода (просроченную поставку переносят именно так) либо сопоставьте склад в кабинете. Помните, что изменение поставки приводит состав к присланному — позиции, которых в документе нет, будут удалены.
- publishAt не указан → черновик, покупателям не виден; указан → публикация в этот момент. Опубликовать черновик можно из кабинета или следующим изменением поставки с publishAt. Пока по поставке висит неотвеченный вопрос о связывании, переданный publishAt не применяется; сама собой такая поставка не опубликуется и после ответа — её публикует человек в кабинете либо следующее изменение поставки с publishAt.
- Если поставка пересекается составом и датой (±3 дня) с заведённой не через обмен — вручную в кабинете или загрузкой прайса, — она создаётся черновиком, а в кабинете появляется предложение связать их. На ваш вызов это не влияет — ответ тот же 201.
Грабли
Опубликованная поставка закрывается платформой САМА. В конце дня expectedAt по времени платформы (московскому), а не по вашему местному: предзаказы становятся заказами, товар встаёт в наличие. Вашего вызова платформа при этом не ждёт — задержку оформляют переносом даты через изменение поставки.
Грабли
Номер, занятый закрытой или отменённой поставкой, не отвечает ошибкой. Корректный документ с таким номером получит 200 и тело старой поставки — новый документ не создаётся, отказа нет. При годовом перезапуске нумерации это молча теряет целую поставку, поэтому исход читают по status и составу items в ответе.
Грабли
Отказ роняет ВЕСЬ документ. Построчного results, как в передаче остатков и цен, здесь нет: одна испорченная позиция отменяет весь вызов с указанием её номера — нумерация с единицы («Позиция 2» — вторая позиция массива items), в отличие от 0-базного index построчных методов. Это относится и к причинам, которые в остатках приходят построчно: несопоставленный склад, совпадение с существующей позицией, негодная запись числа.
Грабли
Позиция с новым кодом публикуется СКРЫТОЙ — до одобрения поставщиком. То же правило, что в передаче остатков: карточка, которой платформа не видела раньше, встаёт в очередь кабинета («Подключённые системы → Новые товары»). Документ при этом создаётся и публикуется как обычно — поставка с частично скрытыми позициями штатна, отказа нет; если скрыты ВСЕ позиции, покупателям она до первого одобрения не анонсируется. Одобрения не требуют позиции, уже видимые на витрине; скрытый (в том числе отклонённый ранее) код снова встаёт в очередь.
Подробности
title и category нужны при первом появлении КОДА
Связывание переносит ваш номер на существующую поставку
Отклонённый документ может оставить след в каталоге
| Код | Когда возникает и что делать |
|---|---|
| 400VALIDATION_ERROR | День даты прихода уже закончился, publishAt не раньше конца дня прихода, несопоставленный код склада, код похож на вашу существующую позицию, нулевая цена или банч, объём меньше банча, повтор кода внутри документа, выход за верхние границы (код длиннее 120 символов, количество больше 100 000, цена больше 10 000 000, банч больше 10 000), длина стебля вне диапазона 0–1000 см, слишком длинные название, страна или плантация. |
| 403BILLING_RESTRICTED | Поставщику приостановлено размещение нового за неоплаченный счёт платформы. |
| 403ACCOUNT_WIND_DOWN | Владелец отказался от условий платформы — аккаунт доводит начатое и новых поставок не заводит.Не повторять: режим снимает только владелец, приняв условия. Приход и отмена уже созданной поставки продолжают работать. |
Изменение поставки
PUT/supplier/integration/supplies
Поставки: полный + действие «Внесение товара»Idempotency-Key не обязателендо 500 позиций в документе
Тело то же, что при создании поставки, с тем же externalCode: платформа приводит поставку к переданному состоянию.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Запись числа
Деньги
Количества
externalCode — ваш код номенклатуры
Атрибуты карточки: длина стебля может быть взята из названия
Дата и время
Ограничение размещения за неоплаченный счёт
Режим завершения дел
| Поле | Обязательность | Описание |
|---|---|---|
| externalCodestring | обяз. | Номер вашего документа; он же защита от дублей. Уникален в пределах компании и не переиспользуется.
|
| warehouseCodestring | обяз. | Склад прихода.
|
| expectedAtdate | обяз. | Ожидаемая дата прихода — конец указанного дня. Сегодняшняя дата допустима; отвергается только та, чей день уже закончился по времени платформы.
|
| publishAtdatetime | опцион. | Когда показать покупателям. Отсутствует = «не менять»: снятия поставки с витрины в контракте нет вовсе.
|
| itemsarray<Позиция> | обяз. | Позиции документа — строки того же вида, что строка остатка, но без warehouseCode: склад один на всю поставку и задан в шапке.Обязательно: Не менее одной позиции.
|
| externalCodestring | обяз. | Код номенклатуры. |
| quantityint | обяз. | Сколько штук вы ждёте — не остаток.
|
| pricePerUnitint | обяз. | Цена за штуку, копейки; строго больше нуля, до 10 000 000 (100 000 ₽). |
| stemsPerBundleint | обяз. | Размер банча, до 10 000. Обязателен в каждой строке.
|
| titlestring | условно | Название сорта — из него создаётся карточка. До 255 символов.Обязательно: В поставках правило мягче, чем в остатках: нужен, только если под кодом нет карточки товара вовсе. |
| categorystring | условно | Категория из справочника категорий.Обязательно: В поставках правило мягче, чем в остатках: нужна, только если под кодом нет карточки товара вовсе. |
| countryOfOriginstring | опцион. | Страна происхождения — фильтр каталога. До 255 символов.
|
| stemLengthCmint | опцион. | Длина стебля, см — фильтр каталога. Диапазон 0–1000.
|
| plantationstring | опцион. | Плантация. До 255 символов. |
| Поле | Приходит | Описание |
|---|---|---|
| iduuid | всегда | Платформенный идентификатор поставки. Хранить не нужно — адресуйтесь своим externalCode. |
| externalCodestring | всегда | Номер вашего документа. |
| statusstring | всегда | draft — черновик, покупателям не виден; scheduled — момент показа назначен, но ещё не наступил; active — показана покупателям; closed — закрыта (приход состоялся); cancelled — отменена. |
| warehouseCodestring | всегда | Ваш код склада по сопоставлению. Пустая строка, если склад поставки ни с одним вашим кодом не сопоставлен. |
| expectedAtdate | всегда | Дата прихода — календарная дата в часовом поясе платформы. |
| publishAtdatetime | иногда | Момент показа покупателям. У черновика без публикации поле отсутствует. |
| itemsarray | всегда | Позиции: externalCode, title, quantity (после округления вниз до целых банчей), pricePerUnit, stemsPerBundle, reservedQuantity.
|
Что гарантирует метод
- Поставка приводится к переданному состоянию по externalCode позиций: новые добавляются, отсутствующие удаляются, совпадающие обновляются.
- Позиции без вашего кода метод не трогает — их завёл человек в кабинете (например, после ответа «связать»). Позиции, уже уехавшие в наличие поштучным приходом, молча пропускаются.
- Добавленная позиция с НОВЫМ кодом подчиняется режиму одобрения, как при создании поставки: по умолчанию она публикуется скрытой и ждёт решения поставщика в кабинете («Подключённые системы → Новые товары»).
- Документ обязан нести хотя бы одну позицию: «обнулить» поставку изменением нельзя — для этого есть отмена поставки.
- 409 означает, что объём уже кому-то обещан — предзаказом либо корзиной покупателя, оформляющего заказ прямо сейчас. Если товар не приедет, урезание оформляется недостачей по конкретным заказам после прихода.
- 403 смотрит на содержимое документа, а не на сам метод: при приостановке размещения и в режиме завершения дел документ, который только убирает позиции, уменьшает количество или меняет цены, применяется как обычно.
Грабли
publishAt отсутствует = «не менять». Снятия поставки с витрины в контракте нет вовсе. Дальше зависит от состояния: черновик переданный момент выводит на витрину; у запланированной к показу новый будущий момент молча переносит показ (200), повтор того же момента ничего не меняет; у уже показанной будущий момент отвечает 409 — предзаказы открыты. Исключение: пока в кабинете висит неотвеченный вопрос о связывании, публикация не применяется — ответ 200, но поставка остаётся черновиком.
Грабли
Шапка документа применяется раньше состава. Если состав отбит 409, новая дата прихода уже сохранена, а покупатели с предзаказами уведомлены о переносе. Так же частично применяется сбой на ДОБАВЛЕНИИ позиций: обновления и удаления идут раньше и к этому моменту сохранены. Повтор того же документа безопасен и доводит состав до переданного, но считать, что «ничего не произошло», нельзя.
Подробности
Отказ роняет весь документ
Расхождение склада — не ошибка
Idempotency-Key здесь не обязателен
| Код | Когда возникает и что делать |
|---|---|
| 400VALIDATION_ERROR | Тело разбирается теми же правилами, что и при создании поставки: день присланной даты прихода уже закончился, publishAt не раньше конца дня прихода, несопоставленный код склада, объём позиции меньше банча, негодная запись числа.Отказ роняет весь документ; сообщение называет номер позиции. |
| 403BILLING_RESTRICTED | Добавление позиции, увеличение количества либо показ или перенос показа ещё не показанной поставки при приостановке размещения. |
| 403ACCOUNT_WIND_DOWN | То же размещение нового (позиция, рост количества, показ ещё не показанной поставки), когда владелец отказался от условий платформы и аккаунт в режиме завершения дел.Со стороны обмена не лечится — сообщите поставщику; документ, который только сокращает состав, проходит. |
| 404NOT_FOUND | Неизвестный externalCode — документ не заводится заново, это именно «не найдено». |
| 409CONFLICT | Количество позиции ниже уже предзаказанного. |
| 409CONFLICT | Удаление позиции с предзаказами. |
| 409CONFLICT | Поставка уже приехала, отменена, либо позиция изменилась параллельно. |
| 409CONFLICT | У уже показанной поставки передан будущий момент показа — предзаказы открыты. |
| 409CONFLICT | Этот же документ прямо сейчас изменяется предыдущим запросом.Единственный 409 этого метода, который повторяют: подождите несколько секунд и пришлите документ ещё раз. Возникает, когда обработка не дождалась ответа и отправила изменение повторно, пока первое ещё выполняется. Параллельно менять один документ нельзя: обе правки считали бы состав по одному снимку и завели бы позицию дважды. |
Приход поставки
POST/supplier/integration/supplies/arrive
Поставки: полный + действие «Внесение товара»
Проведено поступление — поставка приехала.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
externalCode — ваш код номенклатуры
Дата и время
Статусы заказа
| Поле | Обязательность | Описание |
|---|---|---|
| externalCodestring | обяз. | Номер вашего документа поставки. |
Что гарантирует метод
- Предзаказы поставки становятся обычными заказами (pre_order → confirmed) и приходят в ленте заказов как confirmed; товар появляется в наличии.
- Ответ 200 — приход применён; повтор по уже закрытой поставке тоже 200, ретрай безопасен.
Грабли
Опубликованная поставка закрывается платформой САМА. Это происходит в конце дня expectedAt по времени платформы (московскому), а не по вашему местному: предзаказы становятся заказами, товар встаёт в наличие. Восточнее Москвы окно на перенос короче на разницу часов. Если поставка задерживается, перенесите дату методом изменения поставки — иначе платформа посчитает её приехавшей, не дождавшись вашего вызова.
| Код | Когда возникает и что делать |
|---|---|
| 404NOT_FOUND | Неизвестный externalCode. |
| 409CONFLICT | Поставка отменена, либо это черновик.Сначала опубликуйте её или заводите товар методом остатков и цен. |
Отмена поставки
POST/supplier/integration/supplies/cancel
Поставки: полный + действие «Внесение товара»
Поставка не приедет.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
externalCode — ваш код номенклатуры
Статусы заказа
| Поле | Обязательность | Описание |
|---|---|---|
| externalCodestring | обяз. | Номер вашего документа поставки. |
| reasonstring | обяз. | Причина отмены: покупатели с предзаказами получат её в уведомлении.
|
Что гарантирует метод
- Поставка снимается с витрины, предзаказы отменяются с возвратом денег, а позиции поставки убираются из корзин покупателей.
- Ответ 200 — отменена; повторная отмена тоже 200.
| Код | Когда возникает и что делать |
|---|---|
| 400VALIDATION_ERROR | reason пустая. |
| 404NOT_FOUND | Неизвестный externalCode. |
| 409CONFLICT | Поставка уже приехала — отменять нечего (именно 409, а не 404: документ существует). |
Контрагенты
POST/supplier/integration/clients
Заказы: полныйдо 500 контрагентов в пакетеиз коробки 403: по умолчанию у ключа стоит «Заказы: просмотр»приостановка размещения на метод не распространяется
При подключении — весь список; далее при изменениях: появился новый контрагент, изменилась или снята персональная скидка, изменилось название. Отслеживать изменения поштучно не обязательно — допустимо просто отправлять весь список по расписанию (например, раз в сутки). Платформа запоминает ваших клиентов по ИНН и применяет вашу скидку, когда клиент с этим ИНН регистрируется на платформе. Метод требует уровня «Заказы: полный», а у нового ключа по этому разделу стоит просмотр, поэтому из коробки он отвечает 403 PERMISSION_DENIED; уровень переключает владелец в карточке ключа.
Общие правила, которые здесь действуют
Аутентификация — заголовок X-Api-Key
Идемпотентность — заголовок Idempotency-Key
Формат запросов и ответов
Запись числа
Цены: передаёте свою — в заказах приходит цена покупателя
Режим завершения дел
| Поле | Обязательность | Описание |
|---|---|---|
| clientsarray<Контрагент> | обяз. | Список контрагентов — до 500 за запрос. |
| legalNamestring | обяз. | Юридическое название. |
| innstring | обяз. | ИНН: 10 цифр (юрлицо) или 12 (ИП), с верными контрольными цифрами. |
| discountBpsint | опцион. | Персональная скидка в сотых долях процента: 500 = 5 %, 1050 = 10,5 %. Диапазон 0–9999, где 0 = скидку снять. Поле не передано — скидка не меняется.
|
| managerEmailstring | опцион. | Почта вашего сотрудника, ответственного за этого контрагента. Поле не передано — ответственный не меняется; пустая строка — снять ответственного. Сотрудник опознаётся по адресу; если такого адреса на платформе нет, контрагент и его скидка всё равно применяются, а адрес ждёт, пока владелец один раз сопоставит его с сотрудником в кабинете — после этого все строки с ним свяжутся сами.
|
| Поле | Приходит | Описание |
|---|---|---|
| acceptedint | всегда | Сколько строк принято. |
| rejectedint | всегда | Сколько строк отклонено. |
| managersUnmatchedint | всегда | Сколько РАЗНЫХ адресов из managerEmail не удалось сопоставить с сотрудниками платформы. Это не отказ: такие строки приняты целиком и в rejected не входят — ждёт только закрепление контрагента за сотрудником. Ненулевое значение — повод человеку зайти в кабинет и сопоставить адреса. |
| resultsarray<Ошибочная строка> | всегда | Только ошибочные строки; при полном успехе — пустой массив. |
| indexint | всегда | Номер строки в вашем запросе с нуля. |
| innstring | условно | ИНН строки. Вместе с index идентифицирует ошибочную строку — внешнего кода у контрагента нет.Когда приходит: Отсутствует, если строка пришла с пустым или пробельным ИНН. |
| statusstring | всегда | error. |
| codestring | всегда | Построчный код отказа. |
| messagestring | всегда | Текст отказа. |
Форма ответа та же, что у передачи остатков и цен, но ошибочная строка идентифицируется полями index и inn — внешнего кода у контрагента нет.
Что гарантирует метод
- Строки обновляются по ИНН — повторная передача не создаёт дубль.
- Отсутствие контрагента в пакете ничего не меняет: удаления через этот метод нет.
- Скидку, изменённую человеком в кабинете, выгрузка не перезаписывает.
- Название длиннее 200 символов обрезается, а не отвергается: контрагент со скидкой не теряется.
- Пустой список — штатный успех (200, accepted: 0), в отличие от передачи остатков и цен: ночное задание с пустым справочником не должно превращаться в инцидент. А вот 501-я строка отвергает весь запрос.
- Дубли внутри одного пакета схлопываются в одну запись: имя берётся из последней строки, скидка — из последней, которая её указала. При этом обе строки считаются принятыми. Ответственный склеивается тем же правилом «последний, кто высказался».
- Ответственный, названный для ещё не зарегистрированного контрагента, ждёт вместе со строкой: когда покупатель с этим ИНН появится на платформе, он сразу закрепится за нужным сотрудником.
Грабли
Ручную скидку выгрузка не перебивает — и молчит об этом. Скидка, выставленная человеком в кабинете, считается ручной, и ваша строка её не перезаписывает. При этом строка приходит ПРИНЯТОЙ и попадает в счётчик accepted: признака «не применено» в контракте нет.
Подробности
Когда встаёт скидка
Реестр контрагентов общий для всех учётных систем
Телефоны, адреса и контактные лица контрагентов не принимаются
Ответственный сотрудник: как это работает
| Код | Когда возникает и что делать |
|---|---|
| 400BAD_REQUEST | Конверт не разобрался: битый JSON либо clients не массивом. |
| 400VALIDATION_ERROR | Перебор числа строк — в запросе больше 500 контрагентов.Разбить справочник на несколько запросов. |
| 403PERMISSION_DENIED | У ключа по разделу «Заказы» стоит просмотр, а метод требует полного уровня — так отвечает новый ключ из коробки.Уровень переключает владелец в карточке ключа. |
| 403ACCOUNT_WIND_DOWN | Владелец отказался от условий платформы — аккаунт доводит начатое, а новые условия покупателям не назначает. Пакет отклоняется целиком, results не приходит.Не повторять: режим снимает только владелец, приняв условия. |
| Код | Когда возникает и что делать |
|---|---|
| VALIDATION_ERROR | Нарушение в строке: формат и диапазоны полей, в том числе запись числа и диапазон скидки 0–9999. Остальные строки пакета применяются. |
| INVALID_INN | ИНН строки не проходит проверку: не 10 цифр (юрлицо) и не 12 (ИП) либо неверные контрольные цифры. |
Справочник категорий
| Код | Значение |
|---|---|
cut_flowers |
Срезанные цветы |
plants |
Растения |
dried |
Сухоцветы |
artificial |
Искусственные цветы |
supplies |
Флористические материалы |
Иное значение отвергается там, где категория вообще читается, — при первичном заведении позиции: в выгрузке остатков построчным VALIDATION_ERROR внутри 200, в поставках (создание, изменение) — 400 на весь документ. У уже заведённой позиции категория из строки игнорируется, в том числе неверная.
Частые ошибки интеграций
| Ошибка | Правильно |
|---|---|
id заказа в методе позиции (или наоборот) |
Два идентификатора: ready/unready/reject — orderId; shortfall/restore/remove — itemId |
В shortfall.quantity — новое количество к выдаче |
Там размер недостачи |
Цена 45.50 |
Целые копейки: 4550 |
| Количество в банчах | Всегда в штуках |
| Остаток за вычетом броней платформы | Физический остаток |
| Дельта-выгрузка при режиме «весь остаток» (он стоит по умолчанию) | Сначала режим «только изменения» в кабинете, потом дельта. Иначе каждый пакет читается как весь остаток склада, и позиция, не попавшая в две выгрузки подряд, уходит с витрины как распроданная (передача остатков) |
| Порционная выгрузка прайса с паузой между страницами | Прайс до 5 000 строк отправляется одним пакетом. Страницы прайса длиннее идут подряд, с паузой не больше 5 минут и с запасом (паузу платформа меряет между окончаниями обработки страниц), а строки одного склада — на соседних страницах (выгрузка отсортирована по складу). Симптом: после каждой выгрузки часть позиций пропадает с витрины и возвращается через несколько минут, а в журнале обмена у выгрузки стоит «снято по отсутствию N». Лечение: убрать паузы между страницами и отсортировать выгрузку по складу; прерванную выгрузку отправлять заново с первой страницы (передача остатков) |
since из собственных часов |
Из serverTime прошлого ответа |
Новый Idempotency-Key при повторе |
Повтор со старым ключом |
| Повтор при 400/404/409 | Повторять бесполезно. Кроме 409 с кодом IDEMPOTENCY_IN_PROGRESS — его повторяют тем же ключом (идемпотентность) |
Исправленный пакет отправлен со старым Idempotency-Key |
Новый ключ. Со старым ключом изменённое тело даёт 409 «Idempotency-Key уже использован для другого запроса»; сохранённый ответ повторяется только при побайтово совпадающем теле (идемпотентность) |
| Ожидание, что поставка дождётся вашего «прихода» | Опубликованная поставка закрывается сама в конце дня expectedAt — задержку переносят изменением поставки |
Разбор meta.cursor как JSON |
Значение непрозрачно: возвращать как получено |
Проверка подключения
Проверять обмен можно только на тестовом контуре. На боевом это невозможно даже теоретически: созданная «на пробу» партия — это настоящий товар в каталоге, о котором через несколько минут уходит рассылка покупателям, а отозвать рассылку нечем. Кнопки «прогнать тест» в кабинете поэтому нет и не будет; вместо неё — вкладка «Здоровье», которая показывает состояние боевого обмена, ничего не запуская.
Тестовый контур
Тестовый контур https://stg.lotusmarket.ru/api/v1 изолирован от боевых заказов; ключ для него выдаётся отдельно и на боевом контуре не работает (и наоборот).
Симулятор обмена
У нас есть консольный симулятор учётной системы — он ходит в платформу ровно так, как это должна делать ваша обработка: опрос изменений с If-None-Match, действия по заказу, остатки, поставки, контрагенты. Зависимостей у него нет, кроме Go.
Две пользы: пройти чек-лист ниже, не дожидаясь готовности обработки, и увидеть эталонную реализацию правил транспорта (повторы, Idempotency-Key, 429, 401) — они собраны в одном файле, и их можно повторить один к одному. Запросите его у нашего разработчика вместе с ключом тестового контура.
Чек-лист готовности к боевому запуску
Все сценарии проверяются на тестовом контуре.
Заказы
- Новый заказ появляется документом; повторный запуск обработки не создаёт второй документ.
-
readyменяет статус на платформе; повтор с тем жеIdempotency-Keyне дублирует действие. - Недостача: заказ на 10 банчей, трёх не хватило →
shortfallсquantity= 3 банча в штуках, к выдаче 7 банчей. - Недостача с некратным количеством →
400. - Распроведение документа сборки →
unready; повторное проведение →ready. - Заказ в
cancelled/rejectedзакрывает ваш документ. - Замена позиции: старая строка пришла
cancelled, новая с другимitemIdзаведена, сумма сошлась. - Суммы документов считаются по ценам покупателя: строка —
pricePerUnit × (fulfilledQuantity ?? quantity), отменённые строки не в счёт. Равенство «Σamount=totalAmount» не проверяется — расхождение штатно (лента заказов). - Заказ, оплаченный через платформу: по приходу
paidOutAt/paidOutAmountсоздан документ поступления денег.
Остатки и цены
- Изменение остатка и цены отражается на витрине; остаток
0убирает позицию из каталога. То же происходит с остатком меньше одного банча: купить его нельзя, поэтому покупатель такую позицию не видит. Это не ошибка обмена. - Пакет с ошибочной строкой: остальные применены, ошибочная видна в ответе метода (с номером строки и кодом причины) и сохранена в вашем журнале. Позиция такой строки с витрины не уходит — отклонённая строка отсутствием не считается; исключения два: строка с несопоставленным складом и строка, которую не удалось разобрать вовсе.
- Позиции, заведённые на платформе ранее, сопоставлены до первой выгрузки — дублей в каталоге не появилось.
- Режим чтения выгрузки выбран осознанно (кабинет, Подключённые системы → Системы → «Как читать выгрузку остатков»): «весь остаток» — если выгрузка строится из отчёта об остатках, «только изменения» — если из движений регистра «Товары на складах». В первом режиме позиция, пропавшая из двух выгрузок подряд, уходит с витрины; вывод включается после трёх выгрузок, поэтому на тестовом контуре сработает не с первой попытки.
- Новый код: строка принята (
200), а товар в каталоге не виден — он ждёт одобрения на вкладке «Новые товары». После «На витрину» он появляется в каталоге; следующая выгрузка обновляет его остаток и цену как обычно. - Прайс длиннее 5 000 строк уходит несколькими запросами, и после полного цикла витрина не потеряла ни одной позиции: страницы отправлены подряд, с паузой не больше 5 минут и с запасом (паузу платформа меряет между окончаниями обработки страниц), а во вкладке «Здоровье» строка про снятие по отсутствию не показывает снятых за сутки позиций. Проверять обязательно на объёме, который даёт три и более страницы: на двух страницах ошибка не проявляется ни при какой паузе.
- Первая выгрузка большого прайса: часть строк отвечает
NEW_POSITIONS_LIMIT— это не ошибка, такие строки заводятся следующими выгрузками (не больше 150 новых позиций за запрос; строки с ошибкой в данных и с кодом, по которому ждёт решения «похоже на вашу позицию» (пока строка на неё по-прежнему похожа), места не занимают и сразу получают свой отказ). Проверить, что не позже чем через ⌈(число новых позиций + новых отказов «похоже») / 150⌉ выгрузок отложенных строк не осталось и заведены все позиции без ошибок в данных, а обработка не записала отложенные строки в журнал ошибок как битые.
Поставки и контрагенты (если используются)
- Повторная отправка поставки с тем же
externalCodeне создаёт дубль. - После
arriveпредзаказы приходят в ленте какconfirmed. - Выгрузка контрагентов принята; строка с битым ИНН отклонена, остальные применены; новый контрагент доехал. Снятие скидки (
discountBps: 0) проверяется на скидке, выставленной этой же выгрузкой: скидку, назначенную человеком в кабинете, обмен не трогает (выгрузка контрагентов).
Отказоустойчивость
- Обрыв связи посреди цикла: после восстановления ничего не потеряно и не задвоено.
- Опрос
/changesшлётIf-None-Match, на304тело не разбирает иsinceне двигает, а интервал берёт изnextPollAfterSeconds(при304— из заголовкаX-Next-Poll-After) и не ходит чаще него. Реже — можно: если расписание регламента фиксированное, опрос раз в минуту подходит при любом значении поля.