Четыре способа подключить прайс
Выберите способ в кабинете: раздел «Прайс и подключение». Программист нужен только для последнего.
- 01Загружать файл вручнуюПрайс в Excel, обновляете сами. Кнопка «Загрузить прайс» в кабинете.Как подготовить файл
- 02Прайс по ссылкеВаша программа выкладывает файл на сайт, площадка сама забирает его по расписанию.Как настроить ссылку
- 03Прайс по почтеПрограмма или сотрудник отправляет письмо с файлом на персональный адрес.Как настроить почту
- 04Через программу или APIЕсть программист или 1С-интегратор: остатки, цены и заказы обмениваются автоматически.Как получить ключ
Прайс старше 72 ч показывается покупателям с пометкой «под заказ». Заказы подключённых магазинов нужно подтверждать: срок — 2 ч, подробности в разделе «Заказы продавца».
Файл прайса
Подходят CSV, XLSX, XLS, XML (в том числе фид YML) и ZIP с одним таким файлом, до 100 МБ и 500 000 строк. Первая строка — заголовки, порядок колонок любой: площадка узнаёт их по названию, а если не узнала, в кабинете можно сопоставить колонки вручную и запомнить. В книге с несколькими листами выберите лист с прайсом в мастере загрузки.
Колонки
| Поле | Обяз. | Что писать | Заголовки, которые узнаются |
|---|
brand | да | Бренд производителя, как в каталоге: FEBI BILSTEIN, BOSCH | бренд, производитель, марка, фирма, изготовитель |
article | да | Артикул производителя. Храните колонку как текст, чтобы Excel не убрал ведущие нули | артикул, номер, каталожный номер, код производителя, номер детали |
name | да | Наименование детали | наименование, название, товар, описание, name |
price | да | Цена в рублях: 1234,50 или 1234.50. В API — целые копейки, поле price_kop | цена, стоимость, цена продажи, цена руб, розница |
qty | да | Остаток, целое число от 0. Ноль снимает позицию с продажи | остаток, наличие, количество, кол-во, склад остаток |
term_days_min | нет | Срок поставки в днях; 0 — в наличии | срок, срок поставки, срок мин, дней, delivery |
term_days_max | нет | Гарантированный срок в днях | срок макс, гарантированный срок, срок гарант, term max, guaranteed |
term | нет | Срок текстом, если нет числовых колонок: «2-3 дня» | срок текстом |
warehouse | нет | Код склада из кабинета; пусто — склад по умолчанию | склад, город, warehouse, store, stock code |
multiplicity | нет | Кратность продажи, по умолчанию 1 | кратность, партия кратно, упаковка, multiplicity, pack |
min_order | нет | Минимальная партия | минимальная партия, мин заказ, min order, moq |
probability | нет | Вероятность поставки, 0–100 | вероятность, вероятность поставки, probability |
external_id | нет | Ваш внутренний код товара, до 64 символов. По нему API находит позицию | код товара, внутренний код, код у продавца, id, external id |
oem | нет | Оригинальный номер | оригинальный номер, оем, oem, кросс, cross |
group | нет | Группа или категория | группа, категория, group, category |
apply | нет | Применимость | применимость, применяемость, apply, application |
condition | нет | new или used (новая, б/у) | состояние, condition |
vat | нет | Ставка НДС: «НДС 22%», «Без НДС» | ндс, vat |
returnable | нет | Возвратный товар: да или нет | возврат, возвратный, returnable |
warranty_months | нет | Срок гарантии в месяцах, от 1 до 120: «24», «24 мес.», «2 года». Пусто — гарантия по условиям площадки. В API — целое число или null | гарантия, гарантийный срок, срок гарантии, гарантия мес, warranty |
warranty_manufacturer | нет | Гарантия изготовителя детали: да или нет. Указывается вместе со сроком | гарантия изготовителя, гарантия производителя, заводская гарантия, manufacturer warranty, warranty manufacturer |
photo | нет | Ссылка на фото, только https | фото, изображение, картинка, photo, image |
updated_at | нет | Время строки в формате ISO 8601 (только CSV и API) | дата обновления, updated, updated at |
Колонка «код» без колонки «артикул» считается артикулом, а рядом с «артикулом» — вашим кодом товара (external_id). Бренд, которого нет в справочнике, не публикуется: такие строки проверяет менеджер, после проверки они появятся сами.
Пять колонок Docpart и ABCP
Файл без заголовка с колонками по порядку Производитель; Артикул; Наименование; Количество; Цена принимается как есть.
Кодировка и разделитель
UTF-8 (с BOM или без), Windows-1251 и UTF-16 определяются сами. Разделитель — точка с запятой, табуляция, запятая или вертикальная черта.
Ведущие нули в Excel
Excel превращает артикул 0986424815 в число и теряет ноль. Задайте колонке артикулов формат «Текстовый» до вставки данных или сохраните значение как ="0986424815" — площадка уберёт кавычки и знак равенства.
Режим загрузки
«Файл — это весь мой склад»: позиции, которых нет в файле, обнуляются. «Файл — только изменения»: остальное не трогается. Если пропало больше 30 % позиций или цены у многих строк изменились больше чем вдвое, загрузка останавливается для проверки — в кабинете появятся кнопки «Опубликовать всё равно» и «Отменить загрузку».
Прайс по ссылке
- Настройте в своей программе выгрузку прайса в файл на сайт: адрес вида
https://shop.ru/export/k247.csv. - В кабинете: «Прайс и подключение» → «Источники» → «Добавить ссылку на прайс». Вставьте адрес, при необходимости логин и пароль.
- Выберите, как часто забирать файл (не чаще раза в час), режим и склад, нажмите «Сохранить ссылку».
- В карточке источника нажмите «Проверить ссылку» — появятся колонки файла. Первый раз можно нажать «Скачать прайс сейчас», дальше прайс обновляется сам.
- Колонки не нашлись, в книге нужен другой лист или по ссылке свой XML: нажмите «Настроить колонки прайса» или «Настроить поля XML». К-247 скачает файл и покажет мастер, сопоставление запомнится для следующих скачиваний.
Площадка запрашивает файл с заголовками If-None-Match и If-Modified-Since: если файл не изменился, сервер может ответить 304, прайс при этом считается свежим. Представляется как K247-PriceFetcher/1. Адреса внутренних сетей не принимаются. Пароли ссылок хранятся зашифрованными.
Три неудачи подряд — уведомление в кабинет, десять — источник выключается. Ответ 401 или 403 означает неверный логин или пароль.
Прайс по почте
Способ для магазина без программиста: достаточно почты, из которой уходит файл прайса.
- Войдите в кабинет и откройте раздел «Прайс и подключение». Выберите путь «Прайс по ссылке или почте».
- Во вкладке «Источники» создайте источник «Прайс по почте»: укажите склад и режим. Для начала оставьте режим «только изменения».
- Кабинет покажет ваш персональный адрес вида
price+ab12cd34ef56gh78@k247.online. Нажмите «Скопировать адрес». - Отправьте на этот адрес письмо с файлом прайса во вложении (CSV, XLSX, XLS, XML или ZIP). Тема и текст письма не важны. Если прайс выгружает программа (1С, МойСклад), укажите адрес в её настройке отправки.
- Через одну-две минуты во вкладке «Обзор» появится «Прайс обновлён» и число позиций на витрине. Ошибки строк — во вкладке «Прайсы».
Что важно знать
- Адрес личный: не публикуйте его. Если адрес попал к посторонним, нажмите «Сменить адрес» — старый перестанет работать.
- Письмо целиком — не больше 30 МБ. Файл больше — заархивируйте в ZIP.
- В режиме «весь склад» первые три письма ждут вашей проверки: откройте загрузку и нажмите «Опубликовать всё равно».
- Письмо, не прошедшее проверку отправителя (DMARC), не загружается — в кабинет придёт уведомление. Отправляйте с настоящего адреса вашего домена.
- В «Дополнительно» укажите адрес или домен отправителя, например
*@shop.ru. Без него письмо, отправитель которого не подтверждён проверкой DMARC, ждёт вашей проверки: так прайс не заменит посторонний, узнавший адрес. Там же можно ограничить тему и имя файла. По умолчанию берётся любое вложение CSV, XLSX, XLS, XML или ZIP. - Колонки письма не нашлись или во вложении свой XML: нажмите в карточке источника «Настроить колонки прайса» или «Настроить поля XML». К-247 возьмёт файл последнего письма, а если писем ещё не было, попросит образец файла.
- Одно письмо — один прайс для одного склада. Для нескольких складов создайте несколько источников: у каждого свой адрес.
Вставка таблицы из Excel
Способ для прайса, который открыт в Excel или Google Таблицах: сохранять файл в CSV не нужно.
- Выделите в таблице ячейки прайса вместе со строкой заголовков и скопируйте их: Ctrl+C, на Mac — Cmd+C.
- В кабинете откройте «Прайс и подключение» → «Прайсы» → «Загрузить прайс» и нажмите «Вставить таблицу из Excel».
- Щёлкните в поле и вставьте ячейки: Ctrl+V, на Mac — Cmd+V. Под полем появится итог, например «Вставлено: 1 234 строки, 6 колонок».
- Нажмите «Проверить колонки». Дальше всё как с файлом: проверьте, какие колонки нашлись, выберите склад и режим, нажмите «Проверить и загрузить».
Что важно знать
- Вставка превращается в файл
Вставка из буфера ДД.ММ ЧЧ:ММ.tsv: ячейки через табуляцию, кодировка UTF-8. Он появится в истории загрузок во вкладке «Прайсы», как обычный файл.
- Копируется текст, который виден в ячейках. Поэтому артикул
0986424815 сохраняет ведущий ноль, если в таблице он виден с нулём. Цена вида «1 234,50 ₽» распознаётся.
- Ячейки с переводом строки и кавычками переносятся без искажений. Если программа кладёт в буфер только HTML-таблицу, кабинет разберёт и её.
- Лимиты те же, что у файла прайса. Для большой таблицы в поле видны первые 50 строк, загружается вся таблица. Если таблица больше лимита, кабинет попросит загрузить её файлом.
- Если кабинет пишет «Не видим колонок», в буфере текст, а не ячейки. Скопируйте диапазон ячеек в самой таблице.
- На телефоне удобнее загрузить файл: сохраните таблицу в XLSX или CSV и выберите её кнопкой «Выбрать файл прайса».
Для прайса, который меняется каждый день, лучше настроить загрузку по ссылке или почте: тогда прайс обновляется сам.
Прайс в формате XLS (Excel 97–2003)
Файл .xls из старого Excel, 1С или рассылки дистрибьютора загружается как есть. Пересохранять его в XLSX или CSV не нужно.
- В кабинете откройте «Прайс и подключение» → «Прайсы» и нажмите «Загрузить прайс».
- Нажмите «Выбрать файл прайса» и выберите файл
.xls. Подойдёт и ZIP-архив с одним таким файлом.
- Проверьте, какие колонки нашлись по заголовкам, выберите склад и режим. Дальше всё как с файлом XLSX или CSV.
Файл .xls так же принимается во вложении письма, по ссылке и с FTP. Настраивать ничего не нужно: формат определяется по содержимому файла, а не по имени.
Что важно знать
- Подходят книги Excel 97–2003 и Excel 5.0/95, а также файлы, которые 1С, LibreOffice и другие программы сохраняют как «Microsoft Excel 97–2003».
- Прайс берётся с первого видимого листа. Скрытые листы и листы-диаграммы пропускаются. Если в книге несколько листов, мастер загрузки покажет поле «Лист книги с прайсом»: выберите нужный лист, и он запомнится вместе с колонками. У ссылки и почты лист выбирается кнопкой «Настроить колонки прайса» в карточке источника.
- Из ячейки с формулой берётся посчитанное значение, как оно видно в Excel. Дата попадает числом, как в XLSX.
- Артикул
0986424815 сохраняет ведущий ноль, если в ячейке текст. Если артикул записан числом, Excel хранит его без нуля: задайте колонке формат «Текстовый» и введите артикулы заново.
- «Защита листа» и «Защитить книгу» загрузке не мешают. Пароль на открытие файла нужно снять: «Файл» → «Сведения» → «Защита книги» → «Зашифровать с использованием пароля», очистите поле и сохраните файл. Иначе загрузка остановится с ошибкой «Файл защищён паролем».
- Файл XLS — до 50 МБ, но не больше общего лимита 100 МБ. На листе Excel 97–2003 помещается не больше 65 536 строк. Для прайса больше сохраните его в XLSX или CSV.
Если файл не загрузился
- «Файл XLS повреждён или это не Excel 97–2003»: файл обрезан при скачивании или это другой документ. Откройте его в Excel и сохраните как XLSX или CSV.
- «Файл Excel 2.0–4.0 не поддерживается»: файл из очень старой версии Excel. Сохраните его как XLSX.
- «Это веб-страница, а не таблица»: программа сохранила HTML-страницу с расширением
.xls. Откройте её в Excel и сохраните как XLSX или CSV. Текст с табуляцией под именем .xls читается как CSV сам.
- «Слишком много уникальных строк»: в книге больше миллиона разных текстов. Сохраните прайс как CSV.
Прайс в Google Таблицах, на Яндекс Диске и в других облаках
Если прайс лежит в облаке, свой сайт не нужен: К-247 скачивает файл по ссылке «Поделиться» по расписанию, и прайс на площадке обновляется сам.
- Откройте доступ к файлу по ссылке и скопируйте ссылку. Как это сделать в каждом сервисе, написано ниже.
- В кабинете откройте «Прайс и подключение», вкладку «Источники» и нажмите «Добавить ссылку на прайс». Вставьте ссылку как есть. Логин и пароль не нужны.
- Выберите, как часто забирать файл, режим и склад, нажмите «Сохранить ссылку». В карточке источника появится строка «Распознано», например «Google Таблица, лист gid 0».
- Нажмите «Проверить ссылку»: карточка покажет, какой файл нашёлся и какие в нём колонки. Для Яндекс Диска это имя файла и дата изменения.
Google Таблицы
- В таблице нажмите «Настройки доступа». В блоке «Общий доступ» выберите «Все, у кого есть ссылка» и роль «Читатель», затем «Копировать ссылку».
- Нужен определённый лист: откройте его и скопируйте адрес из строки браузера, в нём есть
#gid=…. Если gid в ссылке нет, берётся первый лист.
- Опубликованная таблица тоже подходит: меню «Файл», пункт «Поделиться», затем «Опубликовать в интернете».
- Лист скачивается в CSV так, как значения видны в ячейках: ведущие нули артикулов сохраняются, формулы считает Google.
Google Диск
- Подходит файл прайса CSV, XLSX, XLS, XML или ZIP. Нажмите на файле «Поделиться», выберите «Все, у кого есть ссылка», затем «Копировать ссылку».
- Ссылка на папку не подходит: откройте файл прайса и скопируйте ссылку на него. Google Таблицу на Диске подключайте ссылкой из открытой таблицы.
Яндекс Диск
- Нажмите на файле «Поделиться» и «Скопировать ссылку». Получится адрес вида
https://disk.yandex.ru/i/… или https://disk.yandex.ru/d/….
- Можно дать ссылку на папку: К-247 возьмёт в ней самый новый файл CSV, XLSX, XLS или ZIP. Чтобы всегда брать один и тот же файл, откройте его в папке и скопируйте адрес из строки браузера:
https://disk.yandex.ru/d/…/price.xlsx.
- Если файл не менялся (та же контрольная сумма и дата), он не скачивается заново: прайс просто считается свежим.
- Если в настройках доступа запрещено скачивание, снимите запрет, иначе файл не получить.
Облако Mail.ru (экспериментально)
- Нажмите «Поделиться», включите доступ по ссылке и скопируйте ссылку вида
https://cloud.mail.ru/public/….
- У Облака Mail.ru нет официального способа скачивать файлы по ссылке программой. Если сервис изменит страницу, загрузка остановится с понятной ошибкой. Тогда подключите Яндекс Диск, Google Диск или отправку прайса почтой.
Dropbox
- Нажмите «Поделиться», затем «Копировать ссылку». Ссылка вида
https://www.dropbox.com/scl/fi/…?rlkey=…&dl=0 подходит как есть.
- Ссылка на папку не подходит. Из России Dropbox открывается не всегда: если скачивание не работает, выберите другой сервис.
Если не скачивается
| Код | Что случилось | Что сделать |
CLOUD_NOT_PUBLIC | Сервис вернул страницу входа вместо файла | Откройте доступ «Все, у кого есть ссылка» и нажмите «Проверить ссылку» |
CLOUD_FOLDER | Это ссылка на папку | Откройте файл прайса и скопируйте ссылку на него |
CLOUD_NOT_FOUND | Файл удалён или доступ закрыт | Проверьте, что файл на месте и доступ по ссылке открыт |
CLOUD_DOWNLOAD_FORBIDDEN | Владелец запретил скачивание на Яндекс Диске | Снимите запрет в настройках доступа |
CLOUD_PROVIDER_CHANGED | Облако Mail.ru изменило страницу | Подключите другой сервис или отправку прайса почтой |
CLOUD_PROVIDER_DISABLED | Сервис на площадке временно не принимается | Загрузите прайс файлом, по почте или ссылкой другого сервиса |
Облачная ссылка работает как обычная ссылка на прайс: частота не чаще, чем разрешено на площадке, три неудачи подряд дают уведомление в кабинет, десять выключают источник. Сервис, который временно ограничил запросы, повторяется по расписанию.
Прайс магазина на ABCP
Магазину на платформе ABCP не нужны API и программист: достаточно прямой ссылки на файл экспорта прайса. Дальше К-247 забирает его по расписанию, как обычную ссылку на прайс.
- В панели управления ABCP настройте экспорт прайс-листа по расписанию в файл CSV или XLSX и скопируйте прямую ссылку на файл. Если не нашли настройку, спросите поддержку ABCP о «прямой ссылке на экспорт прайса».
- В кабинете К-247 откройте «Прайс и подключение», вкладку «Источники» и нажмите «Добавить ссылку на прайс». Вставьте ссылку: для адреса на abcp.ru окно покажет подсказку про экспорт ABCP.
- Выберите частоту не чаще, чем ABCP обновляет файл. Если в экспорте весь склад, выберите режим «Файл — это весь мой склад». Укажите склад и нажмите «Сохранить ссылку».
- Нажмите «Проверить ссылку»: карточка покажет колонки файла. Итог первых загрузок смотрите во вкладке «Прайсы».
Колонки
Колонки находятся по заголовкам в первой строке файла. Подходят привычные названия: «Производитель» или «Бренд», «Номер» или «Артикул», «Наименование», «Цена», «Наличие» или «Количество», «Срок», «Кратность». Если в вашем экспорте заголовки другие, переименуйте колонки в настройке экспорта или пришлите образец файла менеджеру К-247.
Почему ссылка, а не API ABCP
- API ABCP для клиента магазина ищет по отдельным артикулам и не отдаёт весь склад, а лимиты запросов быстро заканчиваются. Файл экспорта содержит весь прайс и лимиты не расходует.
- Доступ к панели управления ABCP давать не нужно: по ссылке экспорта доступен только файл прайса.
- Живая проценка и передача заказов в ABCP в этот способ не входят.
Фид Яндекс Маркета (YML) и свой XML
Если у сайта магазина уже есть фид для Маркета, его можно подключить как прайс. Колонки сопоставлять не нужно: К-247 узнаёт YML сам.
Фид YML по ссылке
- Найдите в панели сайта адрес фида для Яндекс Маркета. Обычно это ссылка вида
https://shop.ru/market.yml или https://shop.ru/yandex.xml. Фид, сжатый в .gz, тоже подходит.
- В кабинете откройте «Прайс и подключение» → «Источники» и нажмите «Добавить ссылку на прайс». Вставьте адрес фида.
- Выберите, как часто забирать фид, режим и склад. Нажмите «Проверить ссылку»: в предпросмотре будут товары из фида.
- Сохраните источник. Дальше прайс обновляется сам.
Файл фида можно и прислать письмом, и загрузить в кабинете: «Прайсы» → «Загрузить прайс». Мастер покажет «Узнали фид Яндекс Маркета: колонки сопоставлены сами».
Что берётся из фида
| Поле К-247 | Элемент offer в YML |
| Бренд | vendor |
| Артикул | vendorCode. Если номеров несколько через запятую, берётся первый. offer id — это код товара в магазине, а не номер производителя, поэтому без vendorCode строка не загружается. Если у вас id совпадает с артикулом, включите в мастере «Брать артикул из id товара, если в фиде нет vendorCode» |
| Наименование | name, а если его нет — typePrefix, vendor и model |
| Цена | price в рублях: currencyId — RUR или RUB. Больше двух знаков после точки — округление до копеек |
| Остаток | count. Без count: available="false" или disabled — 0; товар в наличии — по умолчанию 10 шт. и предупреждение в отчёте |
| Код товара | offer id, если он не длиннее 64 символов |
| Группа | название категории из categories по categoryId |
| Оригинальный номер | param с названием «OEM», «Оригинальный номер» или «Кросс» |
| Применимость, состояние | param «Применимость» и «Состояние» (например, «б/у») |
| Фото | первый picture с адресом https |
Свой XML
Выгрузку своей программы в XML загрузите в кабинете: «Прайсы» → «Загрузить прайс». Мастер найдёт в файле повторяющиеся записи и покажет их в поле «Записи товаров в файле», например «Позиция — 12 430 шт.». Если записей несколько видов, выберите нужный.
Дальше сопоставление такое же, как у колонок таблицы, только вместо колонок — поля записи. Поле записывается путём от элемента записи:
Номер — вложенный элемент, Остатки/Склад — элемент глубже;
@Бренд — атрибут записи, Цена/@Значение — атрибут вложенного элемента;
param[@name='Производитель'] — элемент с нужным значением атрибута;
sum(Остатки/Склад/@Количество) — сумма остатков по складам. Если в записи несколько складов, мастер сам предложит такое поле.
Мастер подберёт бренд, артикул, наименование, цену и остаток по названиям полей. Оставьте флажок «Запомнить сопоставление для следующих загрузок», и следующий такой файл загрузится с тем же сопоставлением. Через API v1 путь записей и поля передаются в профиле: {"xml": {"record": "Прайс/Позиция", "fields": ["@Бренд", "Номер", "Наименование", "Цена", "sum(Остатки/Склад/@Количество)"]}, "columns": {"brand": 1, "article": 2, "name": 3, "price": 4, "qty": 5}}.
Что важно знать
- Кодировка — UTF-8 или Windows-1251 из строки
<?xml … encoding="…"?>. Другие кодировки, например KOI8-R, не читаются: сохраните фид в UTF-8.
- Строка с ценой не в рублях пропускается с ошибкой «Цена не в рублях», остальные товары загружаются.
- По ссылке и почтой автоматически загружается фид YML. Свой XML по ссылке или почте сначала сопоставьте: во вкладке «Источники» нажмите в карточке источника «Настроить поля XML». К-247 скачает файл по ссылке (у почты возьмёт файл последнего письма, а если писем ещё не было, попросит образец файла) и откроет мастер. После «Проверить и загрузить» сопоставление запомнится, и следующие скачивания и письма загрузятся сами. Без сопоставления в истории будет ошибка «Откройте мастер загрузки и укажите, где в XML записи товаров».
- Файлы с объявлениями сущностей в DOCTYPE (
<!ENTITY) не загружаются из соображений безопасности. Обычная строка <!DOCTYPE yml_catalog SYSTEM "shops.dtd"> не мешает, внешние файлы К-247 не скачивает.
- Вложенность — до 64 уровней, текст одного элемента — до 1 МБ. Длинные описания, которые не загружаются в К-247, обрезаются и не мешают.
- Сжатый фид
.gz распаковывается на лету. Распакованный файл — не больше предела ZIP-архива, подозрительно сильное сжатие отклоняется.
- Если файл повреждён, в ошибке будет номер строки, например «Файл XML повреждён: строка 6».
Прайс из 1С: обмен с сайтом
Для магазина, который ведёт товары, цены и остатки в 1С: Управление торговлей, УНФ, Комплексная автоматизация, Альфа-Авто. 1С сама отправляет товары, цены и остатки на К-247 по расписанию. Используется стандартный «Обмен с сайтом» (CommerceML 2), программист не нужен.
- В кабинете откройте «Прайс и подключение» → «Источники» и нажмите «Подключить 1С (обмен с сайтом)». Нажмите «Сохранить и выпустить пароль».
- Окно покажет адрес сайта для 1С
https://k-247.ru/api/cml/exchange, имя пользователя вида cml-7h3k9q2m и пароль. Пароль показываем один раз, поэтому сразу перенесите его в 1С. Для этого есть кнопки «Скопировать адрес», «Скопировать имя» и «Скопировать пароль».
- В 1С создайте узел обмена с сайтом. В УТ 11 и КА 2 он обычно находится в разделе «Администрирование» → «Интеграция с другими программами» → «Обмен с сайтами», в УНФ — в разделе «Настройки» → «Интеграции». Путь зависит от версии. Укажите адрес, имя пользователя и пароль.
- В настройках узла включите выгрузку товаров, цен (вид цен) и остатков (склады). Включите сжатие файлов (zip) и выключите выгрузку картинок: картинки К-247 не принимает. Задайте расписание, например раз в час.
- В 1С нажмите «Проверить соединение», затем «Выполнить обмен». Через одну-две минуты в карточке источника появится строка «Последний обмен: 16.09.2026 14:05, принято 12 430 строк». Ошибки строк показаны во вкладке «Прайсы».
- Нажмите «Настроить цены и склады». Выберите вид цен, укажите, куда класть остатки каждого склада 1С и откуда брать бренд и артикул. Новые настройки применятся со следующего обмена. Чтобы применить их сразу, нажмите «Загрузить последний обмен заново».
Что загружается
- Файлы обмена:
import.xml с товарами и offers.xml с ценами и остатками (схемы 2.03–2.08). В схемах 2.09–2.10 цены и остатки приходят в prices.xml и rests.xml. Имена с суффиксами, например import0_1.xml или offers___….xml, тоже подходят.
- Бренд ищется по порядку: свойство, выбранное в «Настроить цены и склады», затем «Изготовитель», затем свойство или реквизит «Производитель», «Бренд», «Марка», «Изготовитель», «Торговая марка». Если бренд не найден, строка не загрузится, а в отчёте загрузки будет ошибка «Не указан бренд».
- Артикул берётся из поля «Артикул». Если оно пустое, можно брать «Код» товара.
- Цена — выбранный вид цен. По умолчанию К-247 берёт первый вид цен со словом «розн» в названии, а если такого нет — первый. Принимаются только цены в рублях. Цена округляется до копейки.
- Остатки всех складов 1С по умолчанию складываются на склад по умолчанию. Можно направить каждый склад 1С на свой склад К-247 или не загружать его. Отрицательный остаток считается нулём, дробный округляется вниз.
- Характеристики товара (модификации) загружаются отдельными строками, к наименованию добавляются значения характеристик. Характеристики с одинаковым артикулом станут одной позицией: загрузится первая. Поэтому задайте модификациям разные артикулы.
- Товары, помеченные в 1С на удаление, не загружаются.
Полная выгрузка и только изменения
- Полная выгрузка в режиме «1С — это весь мой склад» обнуляет позиции, которых в ней нет. Если выгрузка обнуляет или меняет слишком много сразу, она ждёт вашего решения во вкладке «Прайсы».
- Если 1С прислала только изменения, загрузка идёт в режиме «только изменения». Описания товаров К-247 берёт из последней полной выгрузки. Поэтому первый обмен должен быть полным, иначе появится ошибка «1С прислала только цены и остатки, а описаний товаров ещё не было».
- Если в пакете изменений у товара нет остатка, строка пропускается, чтобы остаток не обнулился.
- Если 1С повторно прислала тот же обмен, новая загрузка в истории не появится, а прайс будет считаться свежим.
Что важно знать
- Обмен идёт только по HTTPS. Пароль у нас хранится в виде хэша, поэтому посмотреть его нельзя. Можно только нажать «Выпустить новый пароль»: старый пароль сразу перестанет работать, и новый нужно будет указать в 1С.
- После 5 неверных паролей за 15 минут вход с этого адреса блокируется на 15 минут. Неверное имя пользователя и неверный пароль дают одинаковый ответ.
- Один запрос по умолчанию принимает файл до 50 МБ: большие файлы 1С сама делит на части. Весь обмен ограничен 300 МБ и 500 000 товаров. Если товаров больше, выгружайте их частями или файлом CSV.
- Если 1С не сообщила о конце обмена, К-247 загрузит полученные файлы через минуту тишины.
- Заказы К-247 в 1С пока не выгружаются: на запрос заказов 1С получает пустой список.
- Тексты ошибок видны в протоколе обмена 1С: «Неверное имя пользователя или пароль обмена К-247», «Слишком много попыток входа, повторите через 15 минут», «Источник выключен в кабинете К-247», «Сеанс обмена истёк — запустите обмен заново», «Файл больше 50 МБ — уменьшите размер файла в настройках обмена 1С».
- Выгрузку из 1С можно загрузить и вручную. Сохраните
import.xml и offers.xml в один ZIP-архив и загрузите его кнопкой «Загрузить прайс».
Прайс с FTP
Способ для прайса, который ваша программа или дистрибьютор выкладывает на FTP-сервер. К-247 сам забирает свежий файл по расписанию.
- В кабинете откройте «Прайс и подключение» → «Источники» и нажмите «Подключить FTP».
- Укажите сервер, например
ftp.shop.ru, шифрование, порт, логин и пароль. Для анонимного входа оставьте логин пустым.
- Укажите папку, например
/export, и имя файла или маску. По маске price_*.xlsx К-247 возьмёт самый новый подходящий файл.
- Выберите, как часто скачивать файл (не чаще раза в час), режим и склад. Нажмите «Проверить подключение». Появится до 20 самых новых файлов папки и строка, какой файл будет взят, например «Подключение работает. В папке 3 файла, возьмём price_0916.xlsx (1,0 МБ, 16.09 06:00)».
- Нажмите «Сохранить FTP-источник». Первое скачивание пройдёт в ближайшую минуту, дальше прайс обновляется сам. Итог каждой загрузки виден во вкладке «Прайсы».
Шифрование
- FTPS (рекомендуем) — обычный порт 21, шифрование включается командой AUTH TLS. Логин, пароль и сам файл передаются зашифрованными.
- FTPS, порт 990 — сервер сразу ждёт шифрованное соединение (неявный FTPS).
- Без шифрования — пароль передаётся открыто. Используйте этот вариант, только если сервер не умеет FTPS. Менеджер площадки может запретить такой режим.
- Сертификат сервера проверяется. Если у сервера самоподписанный сертификат, проверка подключения покажет его отпечаток SHA-256. Сверьте отпечаток с данными хостинга и вставьте его в «Дополнительно» → «Отпечаток сертификата SHA-256».
Что важно знать
- Файл тот же: если имя, размер и дата файла не изменились, К-247 его не скачивает. Прайс при этом считается свежим. Если файл перезаписали тем же содержимым, новой загрузки в истории тоже не будет.
- Форматы и колонки те же, что у файла прайса. Предел размера тоже тот же. Файл больше предела не скачивается, в кабинете появится понятная ошибка.
- Порты: 21, 990, 2121 и 2221. Работает только пассивный режим. Соединение для файла идёт на тот же адрес, что и управляющее, даже если сервер за NAT сообщает внутренний адрес.
- Если на FTP-сервере включён белый список адресов, добавьте в него IP сервера К-247. Адрес показан в итоге проверки подключения. Если адрес там не показан, его подскажет менеджер площадки.
- Если имена файлов на сервере в кодировке Windows-1251 (старые серверы Windows), включите «Имена файлов в кодировке Windows-1251».
- Три неудачи подряд — уведомление в кабинет, десять — источник выключается. Ошибка «FTP-сервер не принял логин или пароль» означает ответ 530.
- Пароль хранится зашифрованным и не показывается. Если вы меняете сервер или логин, пароль нужно ввести заново.
- SFTP пока не поддерживаем. Выгружайте прайс на FTPS, по ссылке HTTPS или почтой.
Прайс из МойСклад
Для магазина, который ведёт товары, цены и остатки в МойСклад. К-247 сам забирает ассортимент по токену и обновляет прайс по расписанию: выгружать файлы не нужно.
- В МойСклад заведите отдельного пользователя с правом только на просмотр товаров и остатков. Токен действует с правами пользователя, поэтому К-247 не сможет ничего изменить в вашем учёте.
- Войдите в МойСклад под этим пользователем и выпустите токен: «Настройки», затем «Токены», затем «Добавить токен». Скопируйте токен.
- В кабинете К-247 откройте «Прайс и подключение», вкладку «Источники», и нажмите «Подключить МойСклад». Вставьте токен и нажмите «Проверить подключение».
- Кабинет покажет виды цен, склады и доп. поля из МойСклад. Выберите цену продажи, доп. поле с брендом, откуда брать артикул («Артикул» или «Код»), какой остаток брать и на какие склады К-247 класть остатки складов МойСклад. Укажите, как часто обновлять: от раза в час до раза в двое суток.
- Нажмите «Сохранить и загрузить прайс». Итог появится во вкладке «Прайсы», а в карточке источника — число товаров и время обновления.
Что загружается
- Товары не из архива, у которых заполнен артикул (или код) и есть цена выбранного вида. Модификации, комплекты и услуги в этой версии не загружаются. Сколько товаров пропущено без цены и без артикула, показывает карточка источника.
- Бренд берётся из выбранного доп. поля: строки или элемента справочника. Если поле с брендом не выбрано или у товара оно пустое, строка не загрузится, а в отчёте загрузки будет ошибка «Не указан бренд». Новые бренды, как и в файле прайса, проверяет менеджер.
- Цена в МойСклад хранится в копейках, на площадку она приходит в рублях с копейками без округления.
- Остаток по умолчанию — остаток минус резерв, но не меньше нуля. Можно выбрать весь остаток без вычета резерва. Товар без остатка приходит с остатком 0.
- Если склады не сопоставлены, остатки всех складов МойСклад складываются и идут на склад по умолчанию. Если сопоставлены, каждый склад МойСклад идёт на свой склад К-247. Несколько складов МойСклад на один склад К-247 складываются, несопоставленные склады не загружаются.
- Наименование, группа товара и код товара в МойСклад сохраняются вместе с позицией.
Что важно знать
- Токен хранится зашифрованным и в кабинете больше не показывается. Чтобы сменить токен, откройте «Изменить настройки» и вставьте новый.
- К-247 обращается только к
api.moysklad.ru и соблюдает ограничения МойСклад: запросы идут по очереди, не больше 9 единиц за 3 секунды, отчёт остатков весит 5 единиц. Поэтому 10 000 товаров загружаются примерно за полминуты, 300 000 — до 20 минут. За одну загрузку принимается до 300 000 товаров.
- Если в МойСклад ничего не изменилось, прайс считается свежим и новая загрузка в истории не появляется.
- «МойСклад не принял токен» — токен отозван или вставлен с ошибкой: выпустите новый. «Нет права просматривать товары или остатки» — выдайте пользователю токена право на просмотр. Если МойСклад ограничил частоту запросов или не ответил, загрузка повторится по расписанию.
- Три неудачи подряд — уведомление в кабинет, десять — источник выключается. После исправления включите источник снова.
- В режиме «весь склад» позиции, которых больше нет в МойСклад, обнуляются. Если загрузка обнуляет или меняет слишком много сразу, она ждёт вашего решения во вкладке «Прайсы».
Ключи API: тестовый и боевой
Ключ выпускает любой сотрудник компании в кабинете: «Прайс и подключение» → «Для программиста» → «Ключи API». Ключ показывается один раз — сохраните его сразу. О выпуске и отзыве ключа узнают все сотрудники компании.
| Тестовый k247_test_… | Боевой k247_live_… |
|---|
| Кто может выпустить | Любая компания, сразу | Компания с проверенными реквизитами, после принятия «Условий API» |
| Прайсы и offers/batch | Разбор и отчёт без публикации (dry_run) | Публикация на витрине |
| Заказы | Только тестовые заказы песочницы | Настоящие заказы |
| Лимиты | Пятая часть боевых | Полные |
До 5 активных ключей каждого вида на компанию. Права (scopes): offers — прайсы, предложения, склады; orders — заказы; finance — зарезервировано. Для ключа можно задать список IP-адресов. Выпуск первого боевого ключа включает подтверждение заказов. Отзыв действует сразу.
Авторизация
Базовый адрес https://k-247.ru/api/v1, только HTTPS, JSON в UTF-8. Ключ передаётся заголовком — в адресе и теле он не принимается. Cookie браузера API не читает.
curl -s https://k-247.ru/api/v1/ping -H "Authorization: Bearer $K247_TOKEN"
Имена полей — snake_case, идентификаторы — строки, время — ISO 8601 UTC, деньги — целые копейки (price_kop). Неизвестные поля в ответах нужно игнорировать: новые необязательные поля добавляются без смены версии. Каждый ответ несёт заголовок X-Request-Id — назовите его, если пишете в поддержку.
Примеры curl на странице и в OpenAPI написаны для bash: Linux, macOS, Git Bash. В Windows curl получает русские буквы из -d '…' в кодировке cp1251, и площадка отвечает 400 BAD_REQUEST «Тело запроса не в кодировке UTF-8». Тело с русскими буквами сохраните в файл UTF-8 и отправьте curl --data-binary @body.json. Этот ответ 400 за Idempotency-Key не закрепляется: тело файлом можно отправить с тем же ключом.
Спецификация OpenAPI 3.0.3: /api/openapi.json. Импортируйте её в Swagger Editor или Postman.
Ошибки
Все ошибки — один формат. message — по-русски для человека, code стабилен, details необязателен.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "В 2 строках есть ошибки",
"request_id": "req_01J8Z5Q0M2AB",
"details": [
{
"row": 3,
"field": "price_kop",
"code": "PRICE_INVALID",
"message": "Цена — целое число копеек больше 0"
}
]
}
}
| HTTP | code | Когда |
|---|
| 400 | BAD_REQUEST | Неразборчивый JSON или неверный параметр пути |
| 400 | VALIDATION_FAILED | Поля не прошли проверку; построчные ошибки — в details |
| 400 | IDEMPOTENCY_KEY_REQUIRED | Метод требует заголовок Idempotency-Key |
| 401 | UNAUTHORIZED | Нет заголовка Authorization или ключ неверный |
| 401 | KEY_REVOKED | Ключ отозван в кабинете |
| 403 | FORBIDDEN_SCOPE | У ключа нет нужного права (offers или orders) |
| 403 | FORBIDDEN_ROLE | Метод продавца вызван ключом покупателя и наоборот |
| 403 | FORBIDDEN_ENV | Действие недоступно окружению ключа: склады test-ключом, песочница live-ключом |
| 403 | COMPANY_NOT_VERIFIED | Боевой ключ компании, реквизиты которой ещё не проверены |
| 403 | IP_NOT_ALLOWED | Адрес запроса не входит в список адресов ключа |
| 404 | NOT_FOUND | Объекта нет или он чужой — чужое не отличается от несуществующего |
| 405 | METHOD_NOT_ALLOWED | Метод HTTP не поддерживается для этого адреса |
| 409 | IDEMPOTENCY_CONFLICT | Тот же Idempotency-Key, но другое тело запроса |
| 409 | IDEMPOTENCY_IN_PROGRESS | Первый запрос с этим ключом ещё выполняется; повторите через Retry-After |
| 409 | INVALID_STATE | Переход статуса недопустим или повтор решения с другими параметрами |
| 409 | AMBIGUOUS_OFFER | Позиция с этим external_id есть на нескольких складах — укажите warehouse |
| 409 | IMPORT_SUPERSEDED | Остановленная загрузка устарела: после неё пришёл новый прайс |
| 409 | OFFER_CHANGED | Цена или остаток предложения изменились; актуальные данные — в details |
| 409 | OFFER_KEY_EXPIRED | offer_key старше срока действия; повторите поиск |
| 413 | PAYLOAD_TOO_LARGE | Тело запроса больше лимита метода |
| 415 | UNSUPPORTED_MEDIA_TYPE | Неподдерживаемый Content-Type |
| 422 | OUT_OF_STOCK | Нет нужного количества |
| 422 | INSUFFICIENT_FUNDS | Недостаточно средств на балансе |
| 422 | OWN_OFFER | Нельзя заказать собственную позицию |
| 422 | BUSINESS_RULE | Бизнес-отказ; текст причины — в message |
| 429 | RATE_LIMITED | Превышен лимит запросов; ждать Retry-After секунд |
| 429 | IMPORT_QUEUE_FULL | В очереди уже слишком много загрузок компании |
| 500 | INTERNAL | Сбой на стороне площадки; сообщите request_id |
| 503 | UNAVAILABLE | Сервис обновляется или база занята; повторите через Retry-After |
| 409 | LIMIT_EXCEEDED | Достигнут предел: например, 5 вебхуков компании в одном окружении |
| 401 | KEY_OWNER_INACTIVE | Сотрудник, выпустивший ключ, отключён — выпустите новый ключ в кабинете |
| 403 | LEGAL_NEEDS | Администратор компании не принял документы площадки: новые КП, заказы и загрузка прайса боевым ключом недоступны |
| 409 | REQUEST_CLOSED | Заявка не активна или срок истёк |
| 409 | PROPOSAL_NOT_ACTIVE | КП снято, заменено, отклонено или закрыто |
| 409 | PROPOSAL_LIMIT | По заявке собран лимит КП, у продавца действующего КП нет |
| 409 | PROPOSAL_REPLACE_LIMIT | Замена КП через API чаще раза в 5 минут или больше 10 раз по заявке; когда можно — в details |
| 409 | ALREADY_BOUGHT | Позиция заявки уже выкуплена |
| 409 | NEED_EXCEEDED | Количество больше оставшейся потребности позиции |
| 409 | EXCHANGE_ORDER | Заказ из КП биржи не подтверждается и не отклоняется — только отгрузка (ship) |
| 422 | CONTACTS_FORBIDDEN | В тексте заявки или КП найдены контакты: телефон, почта, ссылка, логин; в КП — ещё реквизиты, название, адрес или сотрудники продавца |
Коды строк
В results[].code пакета предложений, в ошибках загрузки прайса и в details[].code.
| code | Когда |
|---|
NOT_FOUND | Строка только с external_id, а такой позиции нет |
KEY_CONFLICT | После смены бренда, артикула или склада позиция совпала бы с другой существующей |
STALE_UPDATED_AT | updated_at строки старше, чем у позиции: строка пропущена |
BRAND_EMPTY | Не указан бренд |
BRAND_UNKNOWN | Бренда нет в справочнике: строка появится после проверки менеджером |
ARTICLE_EMPTY | Не указан артикул |
NAME_EMPTY | Не указано наименование |
PRICE_INVALID | Цена не число или меньше или равна нулю |
PRICE_BELOW_MIN | Цена меньше минимальной (1 ₽) |
PRICE_TOO_BIG | Цена больше допустимой |
QTY_INVALID | Остаток не целое число от 0 |
TERM_UNPARSED | Срок не разобран (предупреждение, строка принята) |
WAREHOUSE_UNKNOWN | Склад с таким кодом не найден |
DUPLICATE_ROW | Та же позиция уже встретилась выше в файле или пакете |
DUPLICATE_EXTERNAL_ID | Тот же external_id на том же складе уже встретился в пакете |
ROW_TOO_LONG | Строка файла длиннее допустимого |
VALUE_TOO_LONG | Значение поля длиннее допустимого |
PHOTO_URL_INVALID | Ссылка на фото не https (предупреждение) |
MULTIPLICITY_VIOLATION | Количество не кратно кратности предложения |
MIN_ORDER_VIOLATION | Количество меньше минимальной партии |
QTY_TOO_BIG | Количество в строке больше допустимого |
SUM_TOO_BIG | Сумма строки слишком большая |
ROW_INVALID | Строка пакета не объект JSON |
UPDATED_AT_INVALID | updated_at строки не время ISO 8601 |
ITEM_UNKNOWN | Строка КП не относится к позиции этой заявки |
VAT_INVALID | НДС строки КП не из списка: vat22, vat5, no_vat |
EXTERNAL_ID_INVALID | Код позиции в программе продавца — латинские буквы, цифры и . _ / - (до 64 символов) |
CONTACTS_FOUND | В поле найден контакт (вид — в kind): телефон, почта, ссылка, логин; в КП — реквизиты (requisites), название или адрес (company), сотрудник (person), расчёты мимо площадки (bypass) |
WARRANTY_INVALID | Гарантия: срок не из 1–120 месяцев; «гарантия изготовителя» без срока (offers/batch, строка КП) — строка не записана |
WARRANTY_IGNORED | Гарантия в файле прайса не разобрана («есть», «нет», «0», «14 дней») — предупреждение: строка принята без срока гарантии |
Лимиты
Два ведра на каждый запрос: ключа и компании (все ключи компании вместе — не больше двойного лимита одного ключа). У тестовых ключей — пятая часть запаса и пополнения, но не меньше 1.
| Класс | Методы | Запас | В минуту |
|---|
default | Все остальные, в том числе sandbox/orders, sandbox/exchange/requests и buyer/orders тестовым ключом | 60 | 120 |
search | buyer/search, buyer/quotes, dictionary/brands | 30 | 60 |
bulk | seller/pricelists, seller/offers/batch, buyer/orders боевым ключом | 5 | 10 |
Заказ покупателя тестовым ключом считается по классу default: у тестового ключа ведро bulk — на 1 запрос, и второй тестовый заказ подряд получал бы 429. Боевой ключ оформляет заказы в пределах bulk.
Каждый ответ с действующим ключом, в том числе 403, 404 и 405: X-RateLimit-Limit (в минуту), X-RateLimit-Remaining, X-RateLimit-Reset (секунд до полного ведра). При 429 — Retry-After. Пакет предложений — до 1 000 строк и 5 МБ, прочие JSON-запросы — до 2 МБ.
Идемпотентность
Заголовок Idempotency-Key (1–128 символов A-Z a-z 0-9 _ - : .) защищает от дублей при сетевых сбоях. Без него методы из первого списка отвечают 400 IDEMPOTENCY_KEY_REQUIRED.
| Idempotency-Key | Методы |
|---|
| Обязателен | POST seller/pricelists, POST seller/offers/batch, POST seller/exchange/requests/{id}/proposals, POST buyer/exchange/requests, POST buyer/exchange/proposals/{id}/accept, POST buyer/orders, POST sandbox/orders, POST sandbox/exchange/requests |
| Рекомендуется | POST seller/imports/{id}/approve и reject, POST seller/orders/{id}/confirm, reject и ship, POST seller/exchange/proposals/{id}/withdraw, POST buyer/exchange/requests/{id}/update и close, POST buyer/exchange/proposals/{id}/reject, POST webhooks, POST webhooks/{id}/test |
| Не действует | GET, PUT seller/warehouses, DELETE: заголовок игнорируется |
- Повтор с тем же ключом и телом возвращает сохранённый ответ с заголовком
Idempotent-Replayed: true. - Тот же ключ с другим телом — 409
IDEMPOTENCY_CONFLICT; первый запрос ещё выполняется — 409 IDEMPOTENCY_IN_PROGRESS. - За ключом закрепляется и ответ с ошибкой, который вернул метод (400
VALIDATION_FAILED, 404, 409, 422): повтор с тем же ключом вернёт ту же ошибку, а исправленное тело с тем же ключом — 409 IDEMPOTENCY_CONFLICT. Исправили запрос — возьмите новый ключ. Ответы 429 и 5xx не закрепляются: повтор с тем же ключом выполнится заново. - Ключ хранится 24 часа, у тестовых и боевых ключей API пространства независимы. Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Постоянные ключи из примеров («zayavka-4471») на следующий день создадут новый объект — берите ключ от своего номера документа и попытки.
- Решения по заказу идемпотентны и без заголовка: повтор confirm с теми же параметрами отвечает 200 и
Idempotent-Replayed: state, с другими — 409 INVALID_STATE. - Для остатков дополнительно передавайте
updated_at строки: старая строка не перетрёт более новую.
Заказы продавца
Заказ подключённого магазина приходит в статусе awaiting_confirmation. Каждую строку нужно подтвердить или отклонить за 2 ч. Строка без ответа через 4 ч отменяется автоматически и засчитывается как отказ. Время считается по рабочим часам магазина: по умолчанию пн–пт 09:00–19:00 по Москве, своё расписание задаётся в кабинете.
Цикл обмена
- Раз в 1–5 минут:
GET /seller/orders?since_seq=N. Обработайте элементы и сохраните change_seq последнего — это N следующего опроса. Пока has_more, запрашивайте дальше с cursor. - Строки в
awaiting_confirmation: POST /seller/orders/{id}/confirm (можно меньшее количество и свой срок) или reject с причиной. - Отгрузили —
POST /seller/orders/{id}/ship с трек-номером и номером УПД. Когда отгружены все строки, заказ закрывается в расчётах.
Опрос по since (время) удобен для отладки, но для синхронизации не гарантирует полноты — используйте since_seq. Покупатель обезличен: код, город и адрес доставки, без названия и телефона.
Показать пример клиента на Node.js
Файл docs/examples/seller-sync.js из поставки площадки. Без зависимостей, Node.js 18 и новее: K247_TOKEN=k247_test_… node seller-sync.js --base https://k-247.ru.
#!/usr/bin/env node
// К-247 API v1 — пример клиента продавца (спец. G.4, F.8, F.12). Без зависимостей, Node.js 18+ (встроенный fetch).
// Написан только по docs/openapi-v1.json и странице https://k-247.ru/api.html.
//
// Что делает:
// 1. GET /ping — ключ, окружение (test|live), права;
// 2. POST /seller/offers/batch — остатки и цены пакетом (test-ключ: разбор без записи, dry_run);
// 3. POST /sandbox/orders — тестовый заказ на свою позицию (только test-ключ). Нужна хотя бы одна
// опубликованная позиция: test-ключ позиций не создаёт (шаг 2 — dry_run),
// поэтому сначала загрузите прайс в кабинете (файл, ссылка, почта) или боевым ключом;
// 4. GET /seller/orders?since_seq= — опрос изменений заказов без потерь (сохраняем change_seq последнего);
// 5. POST /seller/orders/{id}/confirm — подтвердить строки, ожидающие подтверждения;
// 6. POST /seller/orders/{id}/ship — отгрузить подтверждённые строки.
//
// Запуск:
// K247_TOKEN=k247_test_... node seller-sync.js --base http://localhost:3247
// K247_TOKEN=k247_live_... node seller-sync.js --base https://k-247.ru --offers offers.json --state sync-state.json --loop 120
// Параметры:
// --base <url> адрес площадки (без /api/v1), по умолчанию https://k-247.ru
// --offers <file> JSON-массив предложений для offers/batch (формат OfferInput). Без файла test-ключ шлёт два демо-предложения,
// live-ключ пакет не шлёт — чтобы пример случайно не опубликовал демо-остатки.
// --state <file> где хранить since_seq между запусками (по умолчанию — только в памяти)
// --loop <сек> опрашивать заказы бесконечно с этим интервалом (по умолчанию — один проход)
// --no-ship только подтверждать, не отгружать
// Код выхода: 0 — успех, 1 — ошибка (текст, code и request_id — в stderr).
'use strict';
const fs = require('fs');
const crypto = require('crypto');
const args = process.argv.slice(2);
const opt = (name, def) => { const i = args.indexOf(name); return i >= 0 && args[i + 1] !== undefined ? args[i + 1] : def; };
const flag = (name) => args.includes(name);
const BASE = String(opt('--base', 'https://k-247.ru')).replace(/\/+$/, '') + '/api/v1';
const TOKEN = process.env.K247_TOKEN || '';
const STATE_FILE = opt('--state', null);
const LOOP_SEC = Number(opt('--loop', 0)) || 0;
const OFFERS_FILE = opt('--offers', null);
const SHIP = !flag('--no-ship');
if (!/^k247_(test|live)_[a-z0-9]{8}_[A-Za-z0-9_-]{43}$/.test(TOKEN)) {
console.error('Задайте ключ в переменной окружения K247_TOKEN (k247_test_… или k247_live_…). Ключ выпускается в кабинете: «Прайс и подключение» → «Для программиста».');
process.exit(1);
}
const log = (...a) => console.log(new Date().toISOString().slice(11, 19), ...a);
const sleep = (ms) => new Promise((ok) => setTimeout(ok, ms));
class ApiError extends Error {
constructor(status, body, requestId) {
const e = (body && body.error) || {};
super((e.message || 'HTTP ' + status) + ' [' + (e.code || status) + ', request_id ' + (e.request_id || requestId || '—') + ']');
this.status = status; this.code = e.code; this.details = e.details; this.requestId = e.request_id || requestId;
}
}
/**
* Запрос к API v1. Повторяет 429 и 503 по Retry-After и сетевые сбои (до 5 попыток) с тем же Idempotency-Key —
* повтор не создаст дубль. Ошибки 4xx возвращаются исключением ApiError.
*/
async function api(method, path, { body, idem } = {}) {
const headers = { Authorization: 'Bearer ' + TOKEN, Accept: 'application/json' };
if (body !== undefined) headers['Content-Type'] = 'application/json';
if (idem) headers['Idempotency-Key'] = idem;
for (let attempt = 1; ; attempt++) {
let res;
try {
res = await fetch(BASE + path, { method, headers, body: body === undefined ? undefined : JSON.stringify(body) });
} catch (e) {
if (attempt >= 5) throw e;
log('сеть недоступна (' + e.message + '), повтор через ' + attempt * 2 + ' с');
await sleep(attempt * 2000);
continue;
}
const requestId = res.headers.get('x-request-id');
const text = await res.text();
let data = null;
try { data = text ? JSON.parse(text) : null; } catch (e) { data = null; }
if ((res.status === 429 || res.status === 503 || (res.status === 409 && data && data.error && data.error.code === 'IDEMPOTENCY_IN_PROGRESS')) && attempt < 5) {
const wait = Math.min(60, Number(res.headers.get('retry-after')) || attempt);
log(method + ' ' + path + ' → ' + res.status + ', ждём ' + wait + ' с');
await sleep(wait * 1000);
continue;
}
if (res.status >= 400) throw new ApiError(res.status, data, requestId);
return { status: res.status, data, replayed: res.headers.get('idempotent-replayed') };
}
}
// ---------- состояние опроса ----------
function loadState() {
if (!STATE_FILE || !fs.existsSync(STATE_FILE)) return { since_seq: 0 };
try { return Object.assign({ since_seq: 0 }, JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'))); } catch (e) { return { since_seq: 0 }; }
}
function saveState(state) {
if (!STATE_FILE) return;
const tmp = STATE_FILE + '.tmp';
fs.writeFileSync(tmp, JSON.stringify(state));
fs.renameSync(tmp, STATE_FILE); // атомарно: после сбоя остаётся прежний since_seq, заказы придут повторно, но не потеряются
}
// ---------- шаги ----------
async function pushOffers(env) {
let offers;
if (OFFERS_FILE) offers = JSON.parse(fs.readFileSync(OFFERS_FILE, 'utf8'));
else if (env === 'test') {
const now = new Date().toISOString();
offers = [
{ brand: 'FEBI BILSTEIN', article: '01-089', name: 'Термостат', price_kop: 123450, qty: 12, term_days_min: 1, term_days_max: 3, external_id: 'A-778', updated_at: now },
{ brand: 'BOSCH', article: '0986424815', name: 'Колодки тормозные передние', price_kop: 389000, qty: 4, term_days_min: 0, term_days_max: 0, external_id: 'A-779', updated_at: now }
];
} else {
log('offers/batch пропущен: для боевого ключа передайте --offers <файл>');
return null;
}
// Idempotency-Key на логическую операцию: при повторе после сбоя сервер вернёт сохранённый ответ
const idem = 'batch-' + crypto.createHash('sha256').update(JSON.stringify(offers)).digest('hex').slice(0, 32);
const r = await api('POST', '/seller/offers/batch', { body: { offers }, idem });
const b = r.data;
log('offers/batch: получено ' + b.received + ', создано ' + b.created + ', изменено ' + b.updated + ', без изменений ' + b.unchanged +
', пропущено ' + b.skipped + (b.dry_run ? ' (dry_run: test-ключ, без записи)' : '') + (r.replayed ? ' [повтор]' : ''));
(b.results || []).filter((x) => x.status === 'skipped').forEach((x) => log(' строка ' + x.index + ': ' + x.code + ' — ' + x.message));
return b;
}
async function createSandboxOrder() {
let r;
try {
r = await api('POST', '/sandbox/orders', { body: {}, idem: 'sandbox-' + crypto.randomUUID() });
} catch (e) {
// 422: у продавца нет ни одной опубликованной позиции — тестовый заказ оформлять не на что
if (e.status === 422) e.message += ' — загрузите прайс в кабинете или боевым ключом: тестовый ключ позиций не публикует';
throw e;
}
const o = r.data;
log('sandbox/orders: заказ ' + o.number + ' (' + o.order_id + '), статус ' + o.status + ', строк ' + o.lines.length +
(o.confirm_deadline_at ? ', подтвердить до ' + o.confirm_deadline_at : ''));
return o;
}
/** Все изменения заказов после since_seq, страницами по cursor. */
async function pollOrders(state) {
const changed = [];
let cursor = null;
do {
const q = new URLSearchParams({ since_seq: String(state.since_seq), limit: '100' });
if (cursor) q.set('cursor', cursor);
const r = await api('GET', '/seller/orders?' + q.toString());
for (const o of r.data.items) {
changed.push(o);
if (typeof o.change_seq === 'number' && o.change_seq > state.since_seq) state.since_seq = o.change_seq;
}
cursor = r.data.has_more ? r.data.next_cursor : null;
} while (cursor);
return changed;
}
async function confirmOrder(o) {
const lines = o.lines.filter((l) => l.status === 'awaiting_confirmation')
.map((l) => ({ line_id: l.line_id, qty: l.qty })); // здесь ваша логика: меньшее qty, term_days, external_order_ref или reject
if (!lines.length) return o;
const r = await api('POST', '/seller/orders/' + encodeURIComponent(o.order_id) + '/confirm', { body: { lines }, idem: 'confirm-' + o.order_id + '-' + o.change_seq });
log('confirm ' + o.number + ': подтверждено строк ' + lines.length + ', статус ' + r.data.status + (r.replayed ? ' [уже было подтверждено]' : ''));
return r.data;
}
async function shipOrder(o) {
const lines = o.lines.filter((l) => l.status === 'confirmed').map((l) => l.line_id);
if (!lines.length) return o;
const r = await api('POST', '/seller/orders/' + encodeURIComponent(o.order_id) + '/ship',
{ body: { lines, track: 'CDEK 1234567890', upd_number: 'УПД-' + o.number }, idem: 'ship-' + o.order_id + '-' + o.change_seq });
log('ship ' + o.number + ': отгружено строк ' + lines.length + ', статус ' + r.data.status + (r.replayed ? ' [уже было отгружено]' : ''));
return r.data;
}
/** Один проход опроса: подтвердить ожидающие строки и отгрузить подтверждённые. */
async function syncOnce(state, env) {
const changed = await pollOrders(state);
const handled = [];
for (let o of changed) {
if (env === 'live' && o.test) continue;
if (o.lines.some((l) => l.status === 'awaiting_confirmation')) o = await confirmOrder(o);
if (SHIP && o.lines.some((l) => l.status === 'confirmed')) o = await shipOrder(o);
handled.push(o);
}
saveState(state); // since_seq сохраняется после обработки: сбой в середине — повтор тех же заказов, операции идемпотентны
log('опрос: изменённых заказов ' + changed.length + ', since_seq = ' + state.since_seq);
return handled;
}
async function main() {
const ping = (await api('GET', '/ping')).data;
log('ping: env ' + ping.env + ', продавец ' + (ping.company && ping.company.seller_code) + ', права ' + (ping.key.scopes || []).join(',') +
', подтверждение заказов ' + (ping.company && ping.company.confirm_orders ? 'включено' : 'выключено'));
await pushOffers(ping.env);
const state = loadState();
let sandbox = null;
if (ping.env === 'test') sandbox = await createSandboxOrder();
do {
const handled = await syncOnce(state, ping.env);
if (sandbox) {
// проверка сценария песочницы: тестовый заказ найден опросом, подтверждён и отгружен
const mine = handled.find((o) => o.order_id === sandbox.order_id);
if (!mine) throw new Error('тестовый заказ ' + sandbox.order_id + ' не пришёл в опросе since_seq');
const final = (await api('GET', '/seller/orders/' + encodeURIComponent(sandbox.order_id))).data;
const statuses = final.lines.map((l) => l.status);
log('итог тестового заказа ' + final.number + ': статус ' + final.status + ', строки ' + statuses.join(','));
if (SHIP && !statuses.every((s) => s === 'shipped')) throw new Error('строки тестового заказа не отгружены: ' + statuses.join(','));
if (!SHIP && !statuses.every((s) => s === 'confirmed')) throw new Error('строки тестового заказа не подтверждены: ' + statuses.join(','));
sandbox = null;
log('сценарий песочницы пройден: batch → sandbox order → confirm' + (SHIP ? ' → ship' : ''));
}
if (LOOP_SEC > 0) await sleep(LOOP_SEC * 1000);
} while (LOOP_SEC > 0);
}
main().catch((e) => {
console.error('ОШИБКА: ' + e.message);
if (e.details) console.error(JSON.stringify(e.details));
process.exit(1);
});
Биржа: программа продавца
Биржа — вторая витрина площадки. Покупатель публикует заявку («нужен фильтр на MAN TGX, 2 шт., сегодня»), продавцы отвечают коммерческими предложениями (КП), покупатель выбирает и оформляет заказ. Программа делает то же, что сотрудник в кабинете, теми же правилами.
Что нужно
- Ключ с правом
exchange. Другие права для биржи не нужны: заказы из КП такой ключ видит и отгружает. - Регион заявок ключа: «моего региона» (по умолчанию, как лента сотрудника) или «всей России». Выбирается при выпуске ключа и меняется кнопкой «Сменить регион заявок»; текущее значение — в
ping, поле key.exchange_region. - Принятые документы площадки: без них отправка КП боевым ключом отвечает 403
LEGAL_NEEDS, а список недостающего приходит в ping, поле legal_needs.
Цикл обмена
- Подпишитесь на
exchange.* (вебхуки) или опрашивайте GET /seller/exchange/requests?since_seq=N. Сохраняйте change_seq последнего элемента — это N следующего опроса. Номер заявки растёт и от чужих КП, поэтому один опрос показывает и новые заявки, и изменения по старым. - Смотрите
can_propose и cannot_propose_reason: closed, expired, limit (по заявке собран лимит КП), region, bought, replace_limit. - Отправьте КП:
POST /seller/exchange/requests/{id}/proposals с обязательным Idempotency-Key. На одну позицию можно дать несколько вариантов — отдельными строками. mode: "draft" готовит черновик: покупатель его не видит, сотрудник проверит и отправит из кабинета. - Новая отправка заменяет прежнее действующее КП (его статус —
superseded), ссылка — replaces_proposal_id. Чтобы не заменить чужую правку, передайте replaces_proposal_id из поля current_proposal_id заявки: несовпадение — 409 INVALID_STATE. - Покупатель принял — приходит
exchange.proposal.accepted и order.created. Заказ из КП виден в GET /seller/orders?source=exchange. - Заказ из КП не подтверждают: КП — уже ваш ответ.
confirm и reject отвечают 409 EXCHANGE_ORDER, работает только ship. ship отгружает заказ из КП целиком: lines в теле на отгрузку не влияют, но если вы их передали, каждый line_id должен быть из этого заказа, иначе 400 VALIDATION_FAILED. status заказа становится shipped. У строк такого заказа status пустой (null), а shipped_qty — 0, поэтому отгрузку смотрите по status заказа.
# Лента заявок: раз в минуту или по вебхуку exchange.request.created
curl -s "$K247/seller/exchange/requests?since_seq=184000" -H "Authorization: Bearer $K247_TOKEN"
# Коммерческое предложение по заявке: несколько вариантов на одну позицию — отдельными строками
curl -s -X POST $K247/seller/exchange/requests/r_8c1d2e3f4a/proposals -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: kp-2609-412-1" \
-d '{"mode":"send","lines":[{"item_id":"i_1a2b3c4d5e","brand":"MANN","article":"W 950/26","price_kop":123450,"qty":2,"term":"in_stock"}]}'
# Заказ пришёл: подтверждать не нужно, сразу отгрузка
curl -s "$K247/seller/orders?source=exchange" -H "Authorization: Bearer $K247_TOKEN"
Что считает площадка
- Стоимость КП (
value_kop) — не сумма всех строк, а сумма того, что покупатель купит по кнопке «Принять»: по одному варианту на позицию, самому дешёвому за штуку, и не больше нужного количества. Сумма всех строк, как вы её прислали, — lines_sum_kop. - Место —
competition.my_rank из competition.proposals по этой стоимости. Сумм и цен соперников площадка не раскрывает. - Полнота —
coverage: сколько позиций заявки открыто, по скольким у вас есть строка и по скольким вариант закрывает всё количество. - Потребность позиции. Купить больше, чем указано в позиции заявки, нельзя — ни вам, ни покупателю. Строка по уже выкупленной позиции при отправке отклоняется кодом
ALREADY_BOUGHT.
Чего в API нет
Переписки (у заявки есть только счётчик unread_messages — позовите сотрудника), правки отправленного КП на месте (отправьте новое), встречной цены, отказа от заказа из КП. Замена КП через API — не чаще раза в 5 минут и не больше 10 раз по одной заявке (409 PROPOSAL_REPLACE_LIMIT); в кабинете ограничений нет. Новая отправка после снятия своего отправленного КП тоже считается заменой (в заявке — can_propose: false, причина replace_limit и время replace_available_at); снятый черновик, который не отправлялся, заменой не считается.
Что видно о покупателе
Марка, модель, год и VIN машины — да: без них деталь не подобрать. Названия компании, публичного кода, телефона, госномера и адреса получения в API нет — вместо них постоянный псевдоним buyer.ref вида «П-7F3A9C»: один и тот же для этого покупателя во всех его заявках и заказах у вас. В заявке адреса нет; в заказе из КП есть адрес доставки delivery.address: его покупатель указывает при «Принять», а если не указал — это адрес его компании из кабинета. Телефоны, почта и ссылки в заявках и КП запрещены: при отправке — 422 CONTACTS_FORBIDDEN, в старых записях — «[скрыто]». В КП запрещены и сведения о продавце: реквизиты (ИНН, ОГРН, КПП, БИК, счёт, номер карты — kind: "requisites"), название с формой собственности, название и адрес своей компании и складов (company), фамилии своих сотрудников (person): покупатель видит продавца кодом, сведения о продавце — в документах заказа. Предлагать в КП расчёты мимо площадки («платите напрямую продавцу», «минуя площадку», «переведите на карту») тоже нельзя — kind: "bypass"; оплата заказа — только способами, которые площадка предлагает при оформлении. Покупателю тексты КП показываются очищенными с профилем вашей компании: в старых записях название, адрес складов, фамилии сотрудников и предложение с расчётом мимо площадки заменены на «[скрыто]». Фото детали передаётся отдельным полем photo_url строки КП, только с публичных фотохостингов из списка площадки (GET /dictionary/exchange, поле photo_hosts).
Отладка без живых покупателей — «Песочница биржи».
Покупатель: поиск
Поиск по витрине — GET /buyer/search, он доступен любому ключу. Перепроверка условий и заказы — в разделе «Заказы покупателя», заявки и КП — в разделе «Биржа: программа покупателя»; и то, и другое — только организациям.
- По артикулу без бренда:
?article=01089 — ответ mode: brands, список брендов с этим артикулом. Если бренд у артикула один, сразу приходит mode: offers с предложениями. Список брендов при любом их числе — с параметром mode=brands. С параметром similar=1 в списке ещё похожие номера (similar) — номера, которые начинаются с введённого. - Выбрали пару из списка:
?article=01089&brand_id=17 (поля article_norm и brand_id пары) — ответ mode: offers, группы запрошенного артикула и аналогов, в том числе аналогов от поставщиков площадки. - У предложения нет названия продавца и его внутренних идентификаторов: только код продавца, рейтинг, долю отказов, город и
offer_key, действительный 30 минут.
У тестового ключа limit не больше 20. Пометка stale: true и availability: on_order — прайс продавца старше 72 ч.
Заказы покупателя
Заказ по прайсу продавца — тот же путь, что корзина на сайте: найти, перепроверить, оформить. Методы доступны только организациям и ключу с правом orders.
# 1. Найти позицию: подставьте свой артикул и brand_id (offer_key живёт 30 минут)
curl -s "$K247/buyer/search?article=01-089&brand_id=17" -H "Authorization: Bearer $K247_TOKEN"
# 2. Перепроверить перед заказом: цена, остаток, срок
curl -s -X POST $K247/buyer/quotes -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"items":[{"offer_key":"eyJvIjoi...","qty":2}]}'
# 3. Оформить заказ (strict: изменилась цена — заказа не будет)
curl -s -X POST $K247/buyer/orders -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: order-4471" \
-d '{"reference":"ТК-Север-4471","items":[{"offer_key":"eyJvIjoi...","qty":2}],"payment":"invoice","accept_terms":true,"price_policy":"strict"}'
# 4. Следить за заказами: и за магазинными, и за теми, что из КП биржи
curl -s "$K247/buyer/orders?since_seq=0" -H "Authorization: Bearer $K247_TOKEN"
Перепроверка условий
POST /buyer/quotes берёт до 50 offer_key и всегда отвечает 200. Статус каждой строки: ok, price_changed (новая цена в price_kop, прежняя — в previous_price_kop), insufficient_qty, unavailable, invalid_key. Для ok и price_changed приходит свежий offer_key — оформляйте заказ им.
Оформление
price_policy: strict (по умолчанию) — любая изменившаяся цена отменяет заказ с 409 OFFER_CHANGED и актуальными условиями в details; accept_lower — принять, если цена не выросла.accept_terms: true обязателен: это ознакомление с условиями возврата, оно записывается в журнал согласий компании. Без него — 422 BUSINESS_RULE, как у корзины сайта.- Экспресс-доставка —
delivery.type: "express" или delivery.express: true: с ценой экспресса и от порога суммы, как в корзине. Комментарий к заказу — comment (до 500 символов), продавец видит его в заказе. - Тело — JSON в кодировке UTF-8; иначе — 400
BAD_REQUEST. В Windows кириллица в -d '…' уходит в cp1251: сохраните тело в файл UTF-8 и отправьте curl --data-binary @body.json; в полях-справочниках передавайте коды (invoice), а не подписи. - Позиции разных продавцов разъезжаются по отдельным заказам — в ответе их несколько.
- Кратность и минимальная партия из прайса не проверяются — как и в корзине сайта; поля
multiplicity и min_order даются для сведения. - Свою позицию заказать нельзя (422
OWN_OFFER), недостаток остатка — 422 OUT_OF_STOCK, денег на балансе — 422 INSUFFICIENT_FUNDS.
Слежение
GET /buyer/orders?since_seq=N отдаёт заказы обоих видов: из магазина (source: "shop") и по принятым КП биржи (source: "exchange", с блоком exchange и ссылками строк на строки КП). Фильтры — source, status, reference, ids. У отказанных и отменённых строк есть alternatives — до трёх замен с готовым offer_key. Про события заказов — «Вебхуки».
Заказ из КП продавец не подтверждает и отгружает целиком: отгрузку смотрите по status заказа (shipped) и событию order.shipped. У строк такого заказа status пустой (null), shipped_qty — 0. Адрес доставки заказа — delivery.address: из тела «Принять», а без него — адрес компании из кабинета.
Ключ, у которого есть только право exchange, видит здесь лишь заказы из КП.
Биржа: программа покупателя
Организация — транспортная компания, автосервис, магазин или разборка в роли покупателя — публикует заявку из своей программы, получает КП продавцов и оформляет заказ. Автовладельцу эти методы недоступны (403 FORBIDDEN_ROLE): его заказы оформляются в кабинете.
Что нужно
- Ключ с правом
exchange — выпускается в «Кабинете», вкладка «API» (панель «Подключение программы»). - Принятые документы площадки: без них создание заявки, «Принять» и заказ боевым ключом отвечают 403
LEGAL_NEEDS. - Заявки видит вся компания: созданные сотрудниками в кабинете программа тоже получает, и наоборот.
Цикл обмена
POST /buyer/exchange/requests — марка машины обязательна, позиции описываются словами (артикул не нужен). Свой номер заявки — reference, по нему потом ищете. Коды полей (срок, доставка, оплата, группа и рынок машины, регион) — в GET /dictionary/exchange.- Ждите
exchange.proposal.received и exchange.proposal.replaced или опрашивайте GET /buyer/exchange/proposals?exchange_request_id=. Без since_seq приходят действующие КП, с since_seq — все статусы, включая снятые и заменённые. С sort=value площадка сортирует все КП заявки по стоимости и отдаёт первые limit; курсора у этого режима нет, has_more: true — КП больше, чем limit. POST /buyer/exchange/proposals/{id}/accept — «Принять». Без lines площадка соберёт тот же план, что кнопка в кабинете: по одному варианту на позицию, самому дешёвому за штуку, в нужном количестве. С lines вы выбираете строки сами, в том числе два варианта на одну позицию (2 + 2 шт. при потребности 4). accept_terms: true обязателен. Адрес доставки заказа — delivery.address в теле «Принять»; без него в заказ попадает адрес компании из кабинета, а не pickup_address заявки. Способ доставки и оплата без полей delivery.type и payment берутся из заявки.- В ответе — заказы со строками. Дальше они живут в «Заказах покупателя» с
source: "exchange". - Ненужное КП —
reject, заявку целиком — close, правки — update. Повтор этих вызовов отвечает 200 и заголовком Idempotent-Replayed: state.
# 1. Заявка на биржу: Idempotency-Key обязателен
curl -s -X POST $K247/buyer/exchange/requests -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: zayavka-4471" \
-d '{"reference":"ТК-Север-4471","vehicle":{"brand":"MAN","model":"TGX","year":"2016","vin":"WMA06XZZ0FP000001"},"items":[{"name":"Фильтр масляный","qty":2}],"deadline":"today"}'
# 2. КП по заявке, самые дешёвые сверху
curl -s "$K247/buyer/exchange/proposals?exchange_request_id=r_8c1d2e3f4a&sort=value" -H "Authorization: Bearer $K247_TOKEN"
# 3. Принять целиком (без lines) или выбранными строками
curl -s -X POST $K247/buyer/exchange/proposals/o_6a7b8c9d0e/accept -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: accept-4471-1" \
-d '{"lines":[{"proposal_line_id":"ol_92b3c4d5e6","qty":2}],"accept_terms":true}'
Сколько можно купить
Больше, чем указано в позиции заявки, купить нельзя. Если позицию успели купить у другого продавца или другим ключом — 409 ALREADY_BOUGHT; если выбранные строки в сумме дают больше нужного — 409 NEED_EXCEEDED с оставшейся потребностью в details. Нужно больше — исправьте количество в заявке (update). Купленную позицию менять и удалять нельзя, а переименование или удаление позиции закрывает строки КП по ней — продавцы получат exchange.request.updated.
Пределы
Не больше 60 заявок в час на компанию (429 RATE_LIMITED) и 300 действующих заявок, созданных ключами (409 LIMIT_EXCEEDED). У тестового ключа — пятая часть и не больше 20 действующих. Телефоны, почта и ссылки в заявке запрещены (422 CONTACTS_FORBIDDEN): договариваться нужно через площадку.
Что видно о продавце
Код, город, регион, рейтинг и доля отказов — названия компании и её контактов в API нет ни до, ни после заказа: они в документах сделки и в кабинете. Склад в строке заказа — город и обезличенный код вида «ТТ-014-2» (warehouse_code), не название и адрес склада. Внутренних кодов позиций продавца в КП и в заказе покупателя тоже нет. В текстах КП реквизиты, контакты, название и адрес продавца, фамилии его сотрудников и предложения рассчитаться мимо площадки, в названиях позиций — реквизиты и контакты заменены на «[скрыто]».
Отладка без живых продавцов — «Песочница биржи».
Вебхуки
Вебхук — адрес вашей программы, на который К-247 сам отправляет событие, как только что-то изменилось: пришёл заказ, продавец отказал по позиции, загрузка прайса остановлена. Опрашивать API по since_seq можно реже — вебхук подскажет, когда пора.
Как подключить
- Поднимите приёмник: адрес
https://, порт 443, 8443, 80 или 8080. Он проверяет подпись и отвечает 2xx за 10 секунд. - Создайте подписку запросом
POST /webhooks или в кабинете: «Прайс и подключение» → «Для программиста» → «Вебхуки». Сохраните секрет whsec_… — он показывается один раз. - Отправьте проверочное событие
ping: POST /webhooks/{id}/test или кнопкой в кабинете. Итог — в last_delivery и в журнале доставок кабинета.
Окружение подписки — окружение ключа: подписка тестового ключа получает события песочницы (тестовые заказы, проверочные загрузки), боевого — настоящие. У компании до 5 подписок в каждом окружении. На тестовом стенде площадки адрес может быть http://, на k-247.ru — только https://.
События
| Событие | Когда | Что в data |
|---|
order.created | Новый заказ | Продавцу — по его позициям, покупателю — по его заказу |
order.line_confirmed | Продавец подтвердил позиции | В data.line_ids — подтверждённые строки |
order.line_rejected | Отказ по позициям | Причина — data.reason: out_of_stock, partial_stock… |
order.line_cancelled | Позиции отменены | Покупателем, менеджером или по сроку подтверждения (sla_timeout) |
order.shipped | Заказ отгружен | Статус заказа — data.status |
import.finished | Загрузка прайса завершена | data.status: published, failed или rejected, счётчики строк |
import.held | Загрузка остановлена для проверки | Причины — data.held_reason; подтвердите или отклоните загрузку |
price.stale | Прайс устарел | Источник не обновлялся дольше срока — позиции показываются «под заказ» |
exchange.request.created | Новая заявка на бирже | Продавцу, которому заявка видна по правилам ленты и фильтру подписки options |
exchange.request.updated | Заявка изменена | data.reason: edited или items_closed, закрытые строки КП |
exchange.request.closed | Заявка закрыта | data.reason: bought, buyer_closed, expired, manager_closed |
exchange.proposal.received | Пришло КП | Покупателю: КП продавца, код продавца |
exchange.proposal.replaced | КП заменено | Покупателю: новое КП и заменённое |
exchange.proposal.withdrawn | КП снято | Покупателю: КП снято продавцом |
exchange.proposal.accepted | КП принято | Продавцу: заказы и купленные строки, data.scope: full или partial |
exchange.proposal.rejected | КП отклонено | Продавцу: итоговый статус КП |
* | Все события, кроме биржи | Новые типы событий тоже будут приходить — неизвестный тип просто пропустите; события биржи — только по exchange.* |
order.* | Все события заказов | Шаблон по префиксу |
import.* | Все события загрузки прайса | Шаблон по префиксу |
exchange.* | Все события биржи | Нужно право ключа exchange |
Продавцу приходят order.* по его заказам, import.* и price.stale по его прайсам; покупателю — order.* по его заказам.
Конверт и заголовки
Площадка отправляет POST с JSON-конвертом. data минимален: идентификаторы и статусы, полные данные — через GET.
{
"id": "evt_01J8Z6A1B2C3",
"type": "order.line_rejected",
"created_at": "2026-09-15T09:40:00.000Z",
"env": "live",
"api_version": "1",
"data": {
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1190",
"status": "awaiting_confirmation",
"line_ids": [
"l_9a8b7c6d5e"
],
"reason": "out_of_stock"
}
}
| Заголовок | Значение |
|---|
X-K247-Event | Тип события: order.line_rejected |
X-K247-Event-Id | Совпадает с id конверта |
X-K247-Delivery-Attempt | Номер попытки, с 1 |
X-K247-Signature | t=<unix>,v1=<hex HMAC-SHA256(secret, t + "." + тело)> |
User-Agent | K247-Webhooks/1 |
Повторы и выключение
Успех — любой ответ 2xx за 10 секунд. Иначе повтор через 1 мин, 5 мин, 30 мин, 2 ч, 6 ч и 24 ч — всего 7 попыток. Редиректы не выполняются: 3xx считается неуспехом, укажите конечный адрес. Порядок событий не гарантируется, дубли возможны: храните обработанные id не меньше 72 часов.
После 100 неудачных доставок подряд подписка выключается, сотрудникам компании приходит уведомление. Включите её снова в кабинете, когда приёмник заработает. Проверочный ping доходит и до выключенной подписки.
Проверка подписи на Node.js
const crypto = require('crypto');
// rawBody — тело запроса байтами, до JSON.parse; header — значение X-K247-Signature
function verifyK247(rawBody, header, secret) {
const parts = Object.fromEntries(String(header || '').split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!Number.isInteger(t) || !/^[0-9a-f]{64}$/.test(parts.v1 || '')) return false;
if (Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(t + '.').update(rawBody).digest();
return crypto.timingSafeEqual(expected, Buffer.from(parts.v1, 'hex'));
}
Приёмник на Node.js
const http = require('http');
const seen = new Map(); // id события → время обработки; храните не меньше 72 часов (лучше — в своей базе)
http.createServer((req, res) => {
const chunks = [];
req.on('data', (c) => chunks.push(c));
req.on('end', () => {
const raw = Buffer.concat(chunks);
if (!verifyK247(raw, req.headers['x-k247-signature'], process.env.K247_WEBHOOK_SECRET)) { res.writeHead(401); return res.end(); }
const event = JSON.parse(raw);
res.writeHead(200); // ответить быстро: 10 секунд на ответ, обработку — в очередь своей программы
res.end();
if (seen.has(event.id)) return; // повтор той же доставки
seen.set(event.id, Date.now());
// event.type: order.created, order.line_rejected… полные данные — GET /seller/orders/{event.data.order_id}
});
}).listen(8080);
Проверка подписи на PHP
<?php
// $raw — тело запроса как есть; секрет подписки хранится у вас
function verify_k247(string $raw, string $header, string $secret): bool {
$parts = [];
foreach (explode(',', $header) as $p) { [$k, $v] = array_pad(explode('=', $p, 2), 2, ''); $parts[$k] = $v; }
if (!ctype_digit($parts['t'] ?? '') || !preg_match('/^[0-9a-f]{64}$/', $parts['v1'] ?? '')) return false;
if (abs(time() - (int)$parts['t']) > 300) return false;
$expected = hash_hmac('sha256', $parts['t'] . '.' . $raw, $secret);
return hash_equals($expected, $parts['v1']);
}
$raw = file_get_contents('php://input');
if (!verify_k247($raw, $_SERVER['HTTP_X_K247_SIGNATURE'] ?? '', getenv('K247_WEBHOOK_SECRET'))) { http_response_code(401); exit; }
$event = json_decode($raw, true);
// повтор той же доставки: id уже обработан — ответить 200 и ничего не делать
http_response_code(200);
Список подписок — GET /webhooks, удаление — DELETE /webhooks/{id}.
Песочница
Отдельного адреса нет: песочница — это тестовый ключ k247_test_… на том же https://k-247.ru/api/v1. Поле env в ответе ping показывает, какой ключ используется. Тестовые действия не трогают витрину, остатки, резервы, балансы и рейтинг отказов.
| Действие | Боевой ключ | Тестовый ключ |
|---|
ping, справочники, buyer/search | Реальные данные | Реальные данные, только чтение |
pricelists, offers/batch, DELETE offers | Публикация | Разбор и отчёт, dry_run, без записи |
PUT warehouses | Запись | 403 FORBIDDEN_ENV |
sandbox/orders | 403 FORBIDDEN_ENV | Тестовый заказ на свою позицию |
seller/orders, confirm, reject, ship | Настоящие заказы | Только тестовые заказы своей компании, без денег и остатков |
buyer/orders | Настоящие заказы | Тестовый заказ служебному продавцу: робот подтверждает и отгружает |
| Журнал в кабинете | Окружение live | Окружение test |
| Лимиты | Полные | Пятая часть, в поиске limit до 20 |
| Пределы на компанию | — | Тестовых заказов — 50 действующих (409 LIMIT_EXCEEDED) и 200 за сутки (429 RATE_LIMITED), в заказе покупателя — до 20 позиций; тестовых заявок биржи — 20 действующих и 100 за сутки |
Тестовые заказы удаляются через 7 дней после создания.
Перед тестовым заказом
Тестовый заказ оформляется на вашу опубликованную позицию, а тестовый ключ позиций не создаёт: offers/batch и pricelists с ним только проверяют данные. Поэтому сначала нужна хотя бы одна позиция на витрине: загрузите прайс в кабинете (файлом, по ссылке или по почте) или отправьте пакет боевым ключом. Если позиций нет, шаг 3 ответит 422 BUSINESS_RULE.
Сценарий из пяти команд
# 0. Тестовый ключ из кабинета
export K247_TOKEN=k247_test_...
export K247=https://k-247.ru/api/v1
# 1. Ключ работает, env = test
curl -s $K247/ping -H "Authorization: Bearer $K247_TOKEN"
# 2. Пакет предложений: разбор без записи, dry_run = true
curl -s -X POST $K247/seller/offers/batch -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: sandbox-batch-1" -d '{"offers":[{"brand":"FEBI BILSTEIN","article":"01-089","name":"Термостат","price_kop":123450,"qty":12,"term_days_min":1,"term_days_max":3,"external_id":"A-778","updated_at":"2026-09-15T08:59:00.000Z"}]}'
# 3. Тестовый заказ на свою позицию: запомните order_id и line_id из ответа
curl -s -X POST $K247/sandbox/orders -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: sandbox-order-1" -d '{}'
# 4. Подтвердить все строки
curl -s -X POST $K247/seller/orders/ORDER_ID/confirm -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{}'
# 5. Отгрузить
curl -s -X POST $K247/seller/orders/ORDER_ID/ship -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"track":"CDEK 1234567890","upd_number":"УПД-1"}'
Тот же сценарий на Node.js — в разделе «Заказы продавца». Тестовый заказ можно создать и кнопкой «Создать тестовый заказ» в кабинете.
Программа покупателя: заказ тестовым ключом
Заказ по прайсу оформляется тем же POST /buyer/orders с offer_key из поиска, как в разделе «Заказы покупателя». С тестовым ключом заказ приходит с test: true, продавец в нём — служебный робот ТЕСТ-000: реальный продавец такого заказа не видит, деньги и остатки не двигаются. Через 30–60 секунд робот подтверждает строки (order.line_confirmed), строку с qty = 13 отклоняет с причиной out_of_stock (order.line_rejected), ещё через 30–60 секунд отгружает подтверждённое (order.shipped). Тестовые заказы считаются по классу лимита default («Лимиты»), поэтому несколько заказов подряд не упираются в 429.
Коллекции Postman нет: Postman и Swagger Editor импортируют спецификацию /api/openapi.json напрямую.
Песочница биржи
Тестовым ключом можно пройти весь цикл биржи без живых людей, денег и остатков. Тестовые заявки и КП не видны ни покупателям, ни продавцам, не двигают баланс, рейтинг и долю отказов, не шлют писем и нумеруются отдельно («ТЕСТ-З-2609-7»). Роботы ходят тактами: шаг занимает 30 секунд, а такт проверяется раз в 30 секунд, поэтому ответ приходит через 30–60 секунд. Закрытые тестовые заявки убираются через 7 дней.
Программа продавца: робот-покупатель
Тестовую заявку создаёт метод POST /sandbox/exchange/requests (или кнопка «Создать тестовую заявку» в кабинете). Заявка приходит только вашей компании: проверка реквизитов, биржа по типу компании и регион в песочнице не сверяются. Сценарий робота задаётся полем scenario и отрабатывает через такт после вашего первого КП. У служебного покупателя нет адреса в кабинете, поэтому в заказе робота адрес доставки delivery.address — «Тестовый адрес, 1».
| scenario | Что делает робот-покупатель | Какие события придут |
|---|
accept | Принимает КП целиком | exchange.proposal.accepted (full), order.created |
accept_partial | Покупает только первую позицию | exchange.proposal.accepted (partial), order.created |
reject | Отклоняет КП, и каждое следующее тоже | exchange.proposal.rejected |
close | Снимает заявку без покупки | exchange.request.closed (buyer_closed) |
expire | Ставит срок через такт — заявку закрывает таймер | exchange.request.closed (expired) |
compete | Робот-продавец ТЕСТ-000 присылает КП на 10 % дешевле, и робот покупает у него | my_rank = 2, затем exchange.request.updated (items_closed) и exchange.request.closed (bought) |
Девять команд от ключа до отгрузки
# 0. Тестовый ключ с правом «Биржа»
export K247_TOKEN=k247_test_...
export K247=https://k-247.ru/api/v1
# 1. Ключ работает: env = test, capabilities.exchange.seller = true
curl -s $K247/ping -H "Authorization: Bearer $K247_TOKEN"
# 2. Подписка на события (на стенде допустим http, в бою — только https)
curl -s -X POST $K247/webhooks -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"url":"https://erp.shop.ru/k247/hook","events":["exchange.*","order.*"]}'
# 3. Робот-покупатель создаёт тестовую заявку для вашей компании
curl -s -X POST $K247/sandbox/exchange/requests -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: sbx-req-1" -d '{"scenario":"accept"}'
# 4. Заявка в ленте
curl -s "$K247/seller/exchange/requests?since_seq=0" -H "Authorization: Bearer $K247_TOKEN"
# 5. Отправить КП (повтор с тем же Idempotency-Key вернёт то же КП)
curl -s -X POST $K247/seller/exchange/requests/REQUEST_ID/proposals -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: sbx-kp-1" \
-d '{"lines":[{"item_id":"ITEM_ID","brand":"MANN","article":"W 950/26","price_kop":123450,"qty":2,"term":"in_stock"}]}'
# 6. Через 30-60 с: вебхуки exchange.proposal.accepted и order.created
# 7. Заказ из КП
curl -s "$K247/seller/orders?source=exchange" -H "Authorization: Bearer $K247_TOKEN"
# 8. Подтверждение недоступно — ожидаемые 409 EXCHANGE_ORDER
curl -s -X POST $K247/seller/orders/ORDER_ID/confirm -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{}'
# 9. Отгрузить
curl -s -X POST $K247/seller/orders/ORDER_ID/ship -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"track":"CDEK 1234567890"}'
Замену КП в песочнице проверяйте не раньше чем через 5 минут после отправки: предел замен действует и для тестового ключа.
Программа покупателя: роботы-продавцы
Отдельного метода не нужно: создайте обычную заявку тестовым ключом и добавьте поле sandbox. На неё отвечают роботы ТЕСТ-000 и ТЕСТ-001. Строки их КП: бренд K247-TEST, артикул TEST-<номер позиции>, цена 1 000 ₽ × номер позиции. Позиция на 13 шт. остаётся без строки (КП покрывает не всё), позиция на 12 шт. получает два варианта: второй на 20 % дороже и со сроком «2-3 дня».
| sandbox.scenario | Что делают роботы-продавцы | Какие события придут |
|---|
standard | Через такт КП от ТЕСТ-000, ещё через такт — замена на 5 % дешевле | exchange.proposal.received, затем exchange.proposal.replaced |
two_sellers | КП от ТЕСТ-000 и ТЕСТ-001; второй дешевле по первой позиции и молчит по остальным | два exchange.proposal.received |
withdraw | КП приходит и через такт снимается | exchange.proposal.received, exchange.proposal.withdrawn |
no_answer | КП не приходит вовсе | — |
expire | КП приходит, а заявка закрывается по сроку | exchange.proposal.received, exchange.request.closed (expired) |
# Тестовая заявка покупателя: сценарий задаётся полем sandbox
curl -s -X POST $K247/buyer/exchange/requests -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: sbx-buy-1" \
-d '{"vehicle":{"brand":"MAN","model":"TGX"},"items":[{"name":"Фильтр масляный","qty":2}],"sandbox":{"scenario":"two_sellers"}}'
# Через 30-60 с: exchange.proposal.received; КП роботов
curl -s "$K247/buyer/exchange/proposals?exchange_request_id=REQUEST_ID" -H "Authorization: Bearer $K247_TOKEN"
# Принять и дождаться order.created, затем через 30-60 с order.shipped
curl -s -X POST $K247/buyer/exchange/proposals/PROPOSAL_ID/accept -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: sbx-acc-1" -d '{"accept_terms":true}'
После «Принять» появляется тестовый заказ: продавцом в нём числится служебная компания площадки, деньги и остатки не двигаются, а через такт заказ отгружается — придёт order.shipped. Тестовых заявок на компанию — не больше 20 действующих для каждой стороны.
Заказы по прайсу в песочнице описаны в разделе «Песочница»: там робот подтверждает строки, отклоняет строку с qty = 13 и отгружает.
Справочник методов
Все методы из OpenAPI с примерами запросов и ответов. В примерах curl подставьте свой ключ в $K247_TOKEN и идентификаторы своих объектов.
Общие
GET/api/v1/ping
Проверка ключа, окружения, прав и лимитов
Право: не нужноЛимит: default
Ответ 200 Ключ действителен
{
"ok": true,
"env": "test",
"company": {
"seller_code": "ТТ-014",
"type": "shop",
"verified": true,
"city": "Санкт-Петербург",
"confirm_orders": true
},
"key": {
"prefix": "k247_test_8f3kq2mz",
"scopes": [
"offers",
"orders"
],
"created_at": "2026-09-15T07:00:00.000Z",
"exchange_region": "own"
},
"rate_limit": {
"default": {
"limit": 24,
"remaining": 11
},
"search": {
"limit": 12,
"remaining": 6
},
"bulk": {
"limit": 2,
"remaining": 1
}
},
"server_time": "2026-09-15T09:12:00.000Z",
"api_version": "1.1",
"capabilities": {
"exchange": {
"enabled": true,
"seller": true,
"buyer": true,
"orders": "all",
"counter_offer": false
}
},
"legal_needs": []
}
Ошибки: 401, 403, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/ping -H "Authorization: Bearer $K247_TOKEN"
Справочники
GET/api/v1/dictionary/fields
Поля прайса v1 (CSV/XLSX/offers.batch)
Право: не нужноЛимит: default
Ответ 200 Спецификация полей
{
"version": "1",
"fields": [
{
"key": "brand",
"required": true,
"type": "string",
"max_length": 60,
"headers": [
"бренд",
"производитель",
"brand"
]
},
{
"key": "price_kop",
"required": true,
"type": "integer",
"min": 100,
"max": 10000000000,
"file_key": "price",
"file_type": "money_rub"
}
],
"file": {
"formats": [
"csv",
"xlsx",
"zip"
],
"encodings": [
"utf-8",
"windows-1251",
"utf-16"
],
"delimiters": [
";",
"\t",
",",
"|"
],
"max_file_mb": 100,
"max_rows": 500000,
"docpart_five_columns": [
"Производитель",
"Артикул",
"Наименование",
"Количество",
"Цена"
]
}
}
Ошибки: 401, 403, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/dictionary/fields -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/dictionary/brands
Канонические бренды и синонимы; бренды по артикулу
Право: не нужноЛимит: search
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
q | query | нет | string | Префикс бренда или синонима |
article | query | нет | string | Бренды, у которых есть предложения или кроссы по артикулу |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Список брендов
{
"items": [
{
"brand_id": 17,
"name": "FEBI BILSTEIN",
"group": null,
"country": "DE",
"kind": "aftermarket",
"aliases": [
"FEBI",
"FEBIBILSTEIN"
]
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/dictionary/brands?q=feb&limit=20" -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/dictionary/statuses
Коды статусов заказа, строки, причин отказа, импорта, событий
Право: не нужноЛимит: default
Ответ 200 Справочник
{
"order_statuses": [
{
"code": "awaiting_confirmation",
"name": "Ожидает подтверждения"
},
{
"code": "cancelled",
"name": "Отменён"
}
],
"line_statuses": [
{
"code": "confirmed",
"name": "Подтверждена"
}
],
"reject_reasons": [
{
"code": "out_of_stock",
"name": "Нет в наличии",
"counts_as_refusal": true
}
],
"import_statuses": [
"awaiting_mapping",
"queued",
"running",
"held",
"publishing",
"published",
"failed",
"rejected"
],
"webhook_events": [
"order.created",
"order.line_confirmed",
"order.line_rejected",
"order.line_cancelled",
"order.shipped",
"import.finished",
"import.held",
"price.stale"
]
}
Ошибки: 401, 403, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/dictionary/statuses -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/dictionary/exchange
Справочник биржи: коды перечислений и подписи, регионы, фотохостинги, пределы
Право: не нужноЛимит: default
Без права ключа. На вход методы биржи принимают код или подпись (без учёта регистра, «ё» и вида тире).
limits — пределы окружения ключа: у test-ключа requests_per_hour — 0,2 от боевого (целое, не меньше 1), active_api_requests — 20.
Ответ 200 Справочник
{
"version": "1",
"exchanges": [
{
"code": "new",
"name": "Новые запчасти"
},
{
"code": "used",
"name": "Б/у запчасти"
},
{
"code": "all",
"name": "Все варианты"
}
],
"vgroups": [
{
"code": "car",
"name": "Легковые",
"markets": true
},
{
"code": "truck",
"name": "Грузовые",
"markets": true
},
{
"code": "special",
"name": "Спецтехника",
"markets": false
},
{
"code": "equip",
"name": "Доп. оборудование",
"markets": false
},
{
"code": "other",
"name": "Иное",
"markets": false
}
],
"markets": [
{
"code": "eu",
"name": "Европа"
},
{
"code": "cn",
"name": "Китай"
},
{
"code": "jp",
"name": "Япония"
},
{
"code": "kr",
"name": "Корея"
},
{
"code": "us",
"name": "США"
},
{
"code": "ru",
"name": "Россия"
}
],
"deadlines": [
{
"code": "today",
"name": "сегодня",
"expires": "end_of_day_msk"
},
{
"code": "days_2_3",
"name": "2-3 дня",
"hours": 72
},
{
"code": "week",
"name": "неделя",
"hours": 168
}
],
"priorities": [
{
"code": "fast",
"name": "быстрее"
},
{
"code": "cheap",
"name": "дешевле"
},
{
"code": "any",
"name": "все"
}
],
"pickups": [
{
"code": "delivery",
"name": "доставка"
},
{
"code": "pickup",
"name": "самовывоз"
}
],
"deliveries": [
{
"code": "main",
"name": "основной"
},
{
"code": "pickup",
"name": "самовывоз"
},
{
"code": "express",
"name": "экспресс (2 часа)"
}
],
"payments": [
{
"code": "balance",
"name": "с баланса"
},
{
"code": "invoice",
"name": "счёт"
},
{
"code": "card_on_delivery",
"name": "картой при получении"
}
],
"vats": [
{
"code": "vat22",
"name": "НДС 22%"
},
{
"code": "vat5",
"name": "НДС 5%"
},
{
"code": "no_vat",
"name": "Без НДС"
}
],
"terms": [
{
"code": "in_stock",
"name": "В наличии",
"days": 0
},
{
"code": "hours_3",
"name": "через 3 часа",
"days": 0
},
{
"code": "tomorrow_am",
"name": "завтра с утра",
"days": 1
},
{
"code": "days_2_3",
"name": "2-3 дня",
"days": 3
},
{
"code": "days_4_5",
"name": "4-5 дней",
"days": 5
},
{
"code": "week",
"name": "через неделю",
"days": 7
},
{
"code": "on_order",
"name": "под заказ",
"days": null
}
],
"regions": [
"Москва",
"Самарская область",
"Санкт-Петербург"
],
"request_statuses": [
"active",
"done",
"closed",
"expired"
],
"proposal_statuses": [
"draft",
"sent",
"partial",
"accepted",
"rejected",
"superseded",
"withdrawn",
"closed",
"done",
"returned"
],
"line_statuses": [
"pending",
"accepted",
"rejected",
"closed"
],
"max_proposals": {
"min": 0,
"max": 50,
"cabinet": [
0
]
},
"photo_hosts": [
"disk.yandex.ru",
"cloud.mail.ru",
"drive.google.com",
"imgur.com"
],
"limits": {
"items_per_request": 100,
"lines_per_proposal": 200,
"variants_per_item": 20,
"accept_lines": 200,
"requests_per_hour": 60,
"active_api_requests": 300,
"replace_min_sec": 300,
"replaces_per_request": 10,
"sandbox_active_requests": 20
}
}
Ошибки: 401, 403, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/dictionary/exchange -H "Authorization: Bearer $K247_TOKEN"
Продавец
POST/api/v1/seller/pricelists
Загрузка файла прайса в конвейер импорта
Право: offersIdempotency-Key: обязателенЛимит: bulk
Загрузка файла прайса в конвейер импорта Юридические ворота (M.2.5): боевой ключ компании, администратор которой не принял документы площадки, — 403 LEGAL_NEEDS.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
mode | query | нет | string: replace | upsert | Режим |
warehouse | query | нет | string | Код склада по умолчанию |
source_id | query | нет | string | Источник компании kind=file|api |
filename | query | нет | string | Имя файла для сырого тела |
Тело запроса multipart/form-data
| Поле | Обяз. | Тип | Описание |
|---|
file | да | string | |
mode | нет | string: replace | upsert | |
warehouse | нет | string | |
profile | нет | string | JSON профиля колонок |
{
"file": "price.csv (файл прайса: CSV, XLSX, XLS, XML или ZIP)",
"mode": "replace",
"warehouse": "MAIN"
}
Ответ 202 Импорт поставлен в очередь
{
"import_id": "imp_7d2f1c9a0b",
"status": "queued",
"env": "live",
"mode": "replace",
"source_id": "src_91ab",
"status_url": "/api/v1/seller/imports/imp_7d2f1c9a0b"
}
Ошибки: 400, 401, 403, 404, 409, 413, 415, 429, 500, 503
Пример curl
curl -s -X POST "https://k-247.ru/api/v1/seller/pricelists?mode=replace&warehouse=MAIN" -H "Authorization: Bearer $K247_TOKEN" -H "Idempotency-Key: price-2026-09-15" -F "file=@price.csv"
GET/api/v1/seller/imports
Импорты компании
Право: offersЛимит: default
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
status | query | нет | string | Статусы через запятую |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Список
{
"items": [
{
"import_id": "imp_7d2f1c9a0b",
"status": "published",
"phase": "zero",
"env": "live",
"channel": "api",
"mode": "replace",
"source_id": "src_91ab",
"filename": "price.csv",
"rows_total": 48210,
"counters": {
"added": 120,
"updated": 3400,
"unchanged": 44580,
"adopted": 0,
"zeroed": 15,
"skipped": 110,
"unknown_brands": 4,
"skipped_newer": 0
},
"held_reason": [],
"created_at": "2026-09-15T09:00:00.000Z",
"finished_at": "2026-09-15T09:01:40.000Z"
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/seller/imports?status=held&limit=20" -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/seller/imports/{id}
Статус и отчёт импорта
Право: offersЛимит: default
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
errors_offset | query | нет | integer | Смещение списка ошибок |
Ответ 200 Отчёт
{
"import_id": "imp_7d2f1c9a0b",
"status": "held",
"phase": "check",
"env": "live",
"channel": "api",
"mode": "replace",
"source_id": "src_91ab",
"filename": "price.csv",
"rows_total": 48210,
"counters": {
"added": 120,
"updated": 3400,
"unchanged": 44580,
"adopted": 0,
"zeroed": 15,
"skipped": 110,
"unknown_brands": 4,
"skipped_newer": 0
},
"held_reason": [
{
"code": "VANISHED",
"value": 42.1,
"limit": 30,
"message": "Пропало 42,1 % позиций (порог 30 %)"
}
],
"created_at": "2026-09-15T09:00:00.000Z",
"finished_at": null,
"format": "csv",
"encoding": "windows-1251",
"delimiter": ";",
"rows_valid": 48100,
"progress": 48210,
"checks": {
"vanished_pct": 42.1,
"price_change_share": 3.2
},
"errors_total": 110,
"errors": [
{
"row": 17,
"field": "brand",
"code": "BRAND_UNKNOWN",
"message": "Бренд «Febi Bilst.» не найден в справочнике",
"raw": "Febi Bilst.;01-089;Термостат;1234,50;12"
}
],
"unknown_brands": [
{
"sample": "Febi Bilst.",
"rows": 12
}
],
"started_at": "2026-09-15T09:00:02.000Z",
"held_expires_at": "2026-09-16T09:00:40.000Z",
"error": null
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/seller/imports/imp_7d2f1c9a0b -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/seller/imports/{id}/approve
Опубликовать импорт в статусе held
Право: offersIdempotency-Key: рекомендуетсяЛимит: default
Повтор approve уже одобренного — 200 с Idempotent-Replayed: state. Публикация пропускает строки, изменённые другим источником после разбора (counters.skipped_newer). Новый импорт того же источника переводит held в rejected (IMPORT_SUPERSEDED). Юридические ворота (M.2.5): боевой ключ компании, администратор которой не принял документы площадки, — 403 LEGAL_NEEDS.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
comment | нет | string | |
{
"comment": "Сезонная распродажа, так и задумано"
}
Ответ 200 Импорт публикуется
{
"import_id": "imp_7d2f1c9a0b",
"status": "publishing",
"phase": "zero",
"env": "live",
"channel": "api",
"mode": "replace",
"source_id": "src_91ab",
"filename": "price.csv",
"rows_total": 48210,
"counters": {
"added": 120,
"updated": 3400,
"unchanged": 44580,
"adopted": 0,
"zeroed": 15,
"skipped": 110,
"unknown_brands": 4,
"skipped_newer": 0
},
"held_reason": [],
"created_at": "2026-09-15T09:00:00.000Z",
"finished_at": null
}
Ошибки: 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/imports/imp_7d2f1c9a0b/approve -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"comment":"Сезонная распродажа"}'
POST/api/v1/seller/imports/{id}/reject
Отклонить импорт в статусе held
Право: offersIdempotency-Key: рекомендуетсяЛимит: default
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
comment | нет | string | |
{
"comment": "Выгрузка неполная"
}
Ответ 200 Импорт отклонён
{
"import_id": "imp_7d2f1c9a0b",
"status": "rejected",
"phase": "zero",
"env": "live",
"channel": "api",
"mode": "replace",
"source_id": "src_91ab",
"filename": "price.csv",
"rows_total": 48210,
"counters": {
"added": 120,
"updated": 3400,
"unchanged": 44580,
"adopted": 0,
"zeroed": 15,
"skipped": 110,
"unknown_brands": 4,
"skipped_newer": 0
},
"held_reason": [],
"created_at": "2026-09-15T09:00:00.000Z",
"finished_at": "2026-09-15T09:01:40.000Z"
}
Ошибки: 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/imports/imp_7d2f1c9a0b/reject -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"comment":"Выгрузка неполная"}'
POST/api/v1/seller/offers/batch
Синхронный upsert до 1000 предложений
Право: offersIdempotency-Key: обязателенЛимит: bulk
Синхронный upsert до 1000 предложений Юридические ворота (M.2.5): боевой ключ компании, администратор которой не принял документы площадки, — 403 LEGAL_NEEDS.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
warehouse | нет | string | |
offers | да | массив object | |
{
"warehouse": "MAIN",
"offers": [
{
"brand": "FEBI BILSTEIN",
"article": "01-089",
"name": "Термостат",
"price_kop": 123450,
"qty": 12,
"term_days_min": 1,
"term_days_max": 3,
"external_id": "A-778",
"updated_at": "2026-09-15T08:59:00.000Z"
},
{
"external_id": "A-779",
"qty": 0
}
]
}
Ответ 200 Результат построчно
{
"dry_run": false,
"received": 3,
"created": 1,
"updated": 1,
"unchanged": 0,
"skipped": 1,
"results": [
{
"index": 0,
"status": "created",
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778"
},
{
"index": 1,
"status": "updated",
"offer_id": "p_77aa01bc3d",
"external_id": "A-779"
},
{
"index": 2,
"status": "skipped",
"offer_id": null,
"external_id": "A-780",
"code": "PRICE_BELOW_MIN",
"field": "price_kop",
"message": "Цена меньше 1 ₽"
}
],
"errors": []
}
Ошибки: 400, 401, 403, 404, 409, 413, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/offers/batch -H "Authorization: Bearer $K247_TOKEN" -H "Idempotency-Key: stock-20260915-0900" -H "Content-Type: application/json" -d '{"warehouse":"MAIN","offers":[{"external_id":"A-779","qty":0,"updated_at":"2026-09-15T08:59:00.000Z"}]}'
GET/api/v1/seller/offers
Предложения компании постранично (синхронизация по since)
Право: offersЛимит: default
Сортировка change_seq ASC при since_seq (синхронизация), иначе updated_at ASC.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
since_seq | query | нет | integer | Объекты с change_seq строго больше; сортировка change_seq ASC. Опрос без потерь: сохранить change_seq последнего элемента. |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
warehouse | query | нет | string | |
external_id | query | нет | string | |
article | query | нет | string | |
stale | query | нет | integer: 0 | 1 | |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Страница
{
"items": [
{
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"brand": "FEBI BILSTEIN",
"brand_id": 17,
"article": "01-089",
"article_norm": "01089",
"name": "Термостат",
"price_kop": 123450,
"currency": "RUB",
"qty": 12,
"qty_reserved": 2,
"qty_available": 10,
"multiplicity": 1,
"min_order": 1,
"term_days_min": 1,
"term_days_max": 3,
"probability": 95,
"warehouse": "СПб-1",
"condition": "new",
"vat": "НДС 22%",
"returnable": true,
"source_id": "src_api01",
"source_kind": "api",
"visible": true,
"stale": false,
"withdrawn": false,
"change_seq": 184223,
"updated_at": "2026-09-15T09:00:01.000Z",
"source_updated_at": "2026-09-15T08:59:00.000Z"
}
],
"next_cursor": "eyJzIjoxODQyMjN9",
"has_more": true
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/seller/offers?since_seq=184000&limit=200" -H "Authorization: Bearer $K247_TOKEN"
DELETE/api/v1/seller/offers/{external_id}
Снять позицию по внешнему коду (остаток 0, отметка withdrawn)
Право: offersЛимит: default
Строка не удаляется: stock = 0 и withdrawn = true; снятая позиция покупателю не показывается, следующий batch или импорт с любым qty её возвращает. external_id в пути — percent-encoded (может содержать / и не-ASCII), 1–64 символа без управляющих. Test-ключ — dry_run.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
external_id | путь | да | string | |
warehouse | query | нет | string | Код склада; обязателен, если позиция с этим external_id есть на нескольких складах (иначе 409 AMBIGUOUS_OFFER) |
Ответ 200 Позиция обнулена
{
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"warehouse": "MAIN",
"qty": 0,
"withdrawn": true
}
Ошибки: 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X DELETE "https://k-247.ru/api/v1/seller/offers/A-778?warehouse=MAIN" -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/seller/warehouses
Склады компании
Право: offersЛимит: default
Ответ 200 Склады
{
"items": [
{
"code": "MAIN",
"name": "Санкт-Петербург",
"city": "Санкт-Петербург",
"city_id": 2,
"region": "Санкт-Петербург",
"country": "RU",
"address": "ул. Софийская, 8",
"is_default": true,
"active": true,
"delivery_terms": {
"extra_days": 0,
"note": ""
},
"offers": 1240
}
]
}
Ошибки: 401, 403, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/seller/warehouses -H "Authorization: Bearer $K247_TOKEN"
PUT/api/v1/seller/warehouses
Полный список складов (upsert по коду)
Право: offersЛимит: default
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
items | да | массив object | |
{
"items": [
{
"code": "MAIN",
"name": "Основной",
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"country": "RU",
"address": "ул. Софийская, 8",
"is_default": true,
"delivery_terms": {
"extra_days": 0
}
}
]
}
Ответ 200 Склады
{
"items": [
{
"code": "MAIN",
"name": "Санкт-Петербург",
"city": "Санкт-Петербург",
"city_id": 2,
"region": "Санкт-Петербург",
"country": "RU",
"address": "ул. Софийская, 8",
"is_default": true,
"active": true,
"delivery_terms": {
"extra_days": 0,
"note": ""
},
"offers": 1240
}
]
}
Ошибки: 400, 401, 403, 409, 429, 500, 503
Пример curl
curl -s -X PUT https://k-247.ru/api/v1/seller/warehouses -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"items":[{"code":"MAIN","name":"Основной","city":"Санкт-Петербург","country":"RU","is_default":true}]}'
GET/api/v1/seller/orders
Заказы продавца (опрос по since)
Право: orders | exchangeЛимит: default
Live-ключ — заказы test=false. Test-ключ — только test-заказы песочницы своей компании (покупатель c_sandbox). Сортировка change_seq ASC при since_seq, иначе updated_at ASC. Ключ с правом exchange без orders видит только заказы из КП (source exchange), остальные — 404.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
status | query | нет | string | Коды статусов через запятую; awaiting_confirmation — есть строки на подтверждении |
source | query | нет | string: shop | exchange | shop — заказы магазина, exchange — заказы из КП биржи |
since_seq | query | нет | integer | Объекты с change_seq строго больше; сортировка change_seq ASC. Опрос без потерь: сохранить change_seq последнего элемента. |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Страница
{
"items": [
{
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1190",
"status": "awaiting_confirmation",
"status_name": "Ожидает подтверждения",
"test": false,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"change_seq": 184230,
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"buyer": {
"code": "Покупатель",
"city": "Санкт-Петербург"
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "с баланса",
"comment": "",
"currency": "RUB",
"total_kop": 246900,
"delivery_kop": 0,
"accounting_total_rub": 2469,
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"name": "Термостат",
"qty": 2,
"confirmed_qty": null,
"shipped_qty": 0,
"price_kop": 123450,
"sum_kop": 246900,
"status": "awaiting_confirmation",
"reject_reason_code": null,
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": null,
"warehouse": "СПб-1",
"external_order_ref": null,
"plate": ""
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
}
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/seller/orders?status=awaiting_confirmation&since_seq=184000" -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/seller/orders/{id}
Заказ продавца
Право: orders | exchangeЛимит: default
Заказ продавца Ключ с правом exchange без orders видит только заказы из КП (source exchange), остальные — 404.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 200 Заказ
{
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1190",
"status": "awaiting_confirmation",
"status_name": "Ожидает подтверждения",
"test": false,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"change_seq": 184230,
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"buyer": {
"code": "Покупатель",
"city": "Санкт-Петербург"
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "с баланса",
"comment": "",
"currency": "RUB",
"total_kop": 246900,
"delivery_kop": 0,
"accounting_total_rub": 2469,
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"name": "Термостат",
"qty": 2,
"confirmed_qty": null,
"shipped_qty": 0,
"price_kop": 123450,
"sum_kop": 246900,
"status": "awaiting_confirmation",
"reject_reason_code": null,
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": null,
"warehouse": "СПб-1",
"external_order_ref": null,
"plate": ""
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
}
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/seller/orders/ord_5e1a2b3c4d -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/seller/orders/{id}/confirm
Подтверждение целиком или построчно
Право: orders | exchangeIdempotency-Key: рекомендуетсяЛимит: default
Идемпотентность по состоянию: если все переданные строки уже в целевом статусе с теми же параметрами, ответ 200 с текущим заказом и заголовком Idempotent-Replayed: state; другой статус или другие параметры — 409 INVALID_STATE, ничего не меняется. Ключ с правом exchange без orders видит только заказы из КП (source exchange), остальные — 404. Заказ из КП биржи не подтверждается и не отклоняется: 409 EXCHANGE_ORDER, в details — {source, proposal_id, actions}.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
lines | нет | массив object | |
{
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"qty": 1,
"term_days": 2,
"external_order_ref": "ЗК-5512"
}
]
}
Ответ 200 Заказ после подтверждения
{
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1190",
"status": "assembling",
"status_name": "В сборке",
"test": false,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"change_seq": 184230,
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"buyer": {
"code": "Покупатель",
"city": "Санкт-Петербург"
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "с баланса",
"comment": "",
"currency": "RUB",
"total_kop": 123450,
"delivery_kop": 0,
"accounting_total_rub": 1234.5,
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"name": "Термостат",
"qty": 2,
"confirmed_qty": 1,
"shipped_qty": 0,
"price_kop": 123450,
"sum_kop": 123450,
"status": "confirmed",
"reject_reason_code": "partial_stock",
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": 2,
"warehouse": "СПб-1",
"external_order_ref": "ЗК-5512",
"plate": ""
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
}
}
Ошибки: 400, 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/orders/ord_5e1a2b3c4d/confirm -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"lines":[{"line_id":"l_9a8b7c6d5e","qty":1,"term_days":2}]}'
POST/api/v1/seller/orders/{id}/reject
Отказ по заказу или строкам
Право: orders | exchangeIdempotency-Key: рекомендуетсяЛимит: default
Идемпотентность по состоянию: если все переданные строки уже в целевом статусе с теми же параметрами, ответ 200 с текущим заказом и заголовком Idempotent-Replayed: state; другой статус или другие параметры — 409 INVALID_STATE, ничего не меняется. Ключ с правом exchange без orders видит только заказы из КП (source exchange), остальные — 404. Заказ из КП биржи не подтверждается и не отклоняется: 409 EXCHANGE_ORDER, в details — {source, proposal_id, actions}.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
reason | нет | string | Значения: out_of_stock, price_changed, discontinued, wrong_item, cannot_deliver, duplicate, other |
comment | нет | string | |
lines | нет | массив object | |
{
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"reason": "out_of_stock",
"comment": "Последнюю продали в рознице"
}
]
}
Ответ 200 Заказ после отказа
{
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1190",
"status": "cancelled",
"status_name": "Отменён",
"test": false,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"change_seq": 184230,
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"buyer": {
"code": "Покупатель",
"city": "Санкт-Петербург"
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "с баланса",
"comment": "",
"currency": "RUB",
"total_kop": 0,
"delivery_kop": 0,
"accounting_total_rub": 0,
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"name": "Термостат",
"qty": 2,
"confirmed_qty": null,
"shipped_qty": 0,
"price_kop": 123450,
"sum_kop": 0,
"status": "rejected",
"reject_reason_code": "out_of_stock",
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": null,
"warehouse": "СПб-1",
"external_order_ref": null,
"plate": ""
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
}
}
Ошибки: 400, 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/orders/ord_5e1a2b3c4d/reject -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"lines":[{"line_id":"l_9a8b7c6d5e","reason":"out_of_stock"}]}'
POST/api/v1/seller/orders/{id}/ship
Отгрузка, в том числе частичная по строкам
Право: orders | exchangeIdempotency-Key: рекомендуетсяЛимит: default
Идемпотентность по состоянию: если все переданные строки уже в целевом статусе с теми же параметрами, ответ 200 с текущим заказом и заголовком Idempotent-Replayed: state; другой статус или другие параметры — 409 INVALID_STATE, ничего не меняется. Ключ с правом exchange без orders видит только заказы из КП (source exchange), остальные — 404. Заказ из КП биржи (source exchange) отгружается целиком: lines на отгрузку не влияют, но переданные line_id должны быть из этого заказа, иначе 400 VALIDATION_FAILED; у строк такого заказа status — null, shipped_qty — 0, отгрузка видна по status заказа (shipped).
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
lines | нет | массив string | Строки для частичной отгрузки. У заказа из КП биржи (source exchange) на отгрузку не влияют — отгружается весь заказ, но каждый line_id должен быть из этого заказа, иначе 400 VALIDATION_FAILED. |
track | нет | string | |
upd_number | нет | string | |
{
"lines": [
"l_9a8b7c6d5e"
],
"track": "CDEK 1234567890",
"upd_number": "УПД-2609-1190"
}
Ответ 200 Заказ после отгрузки
{
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1190",
"status": "shipped",
"status_name": "Отгружен",
"test": false,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"change_seq": 184230,
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"buyer": {
"code": "Покупатель",
"city": "Санкт-Петербург"
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "с баланса",
"comment": "",
"currency": "RUB",
"total_kop": 246900,
"delivery_kop": 0,
"accounting_total_rub": 2469,
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"name": "Термостат",
"qty": 2,
"confirmed_qty": 2,
"shipped_qty": 2,
"price_kop": 123450,
"sum_kop": 246900,
"status": "shipped",
"reject_reason_code": null,
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": null,
"warehouse": "СПб-1",
"external_order_ref": null,
"plate": ""
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
}
}
Ошибки: 400, 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/orders/ord_5e1a2b3c4d/ship -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"track":"CDEK 1234567890"}'
Биржа: продавец
GET/api/v1/seller/exchange/requests
Лента заявок покупателей (набор — как у сотрудника в кабинете), синхронизация по since_seq
Право: exchangeЛимит: default
Набор: заявки не своей компании, видимые продавцу (реквизиты проверены, биржа по типу компании, регион заявки = регион компании у ключа own) или с КП компании в любом статусе; test-ключ — только тестовые заявки робота для своей компании. Без since_seq и status — действующие заявки и заявки с КП компании; с since_seq — весь набор в любом статусе. Покупатель — псевдоним buyer.ref; госномера и адреса получения нет, свободный текст очищен от контактов.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
status | query | нет | string | Через запятую: active, done, closed, expired или all |
exchange | query | нет | string: new | used | |
vgroup | query | нет | string | Группа техники из справочника. Заявки группы «Иное» (other) проходят фильтр любой группы; vgroup=other — только они Значения: car, truck, special, equip, other |
market | query | нет | string | Рынок из справочника. Фильтр проходят и заявки с рынком «Иное» — легковые и грузовые без рынка (market = null), и заявки группы «Иное» Значения: eu, cn, jp, kr, us, ru |
brand | query | нет | string | Марка без учёта регистра |
region | query | нет | string: own | all | own — сузить ленту ключа all; all ключом own — 400 |
answered | query | нет | string: true | false | true — только с КП компании, false — без КП |
since_seq | query | нет | integer | Объекты с change_seq строго больше; сортировка change_seq ASC. Опрос без потерь: сохранить change_seq последнего элемента. |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Страница заявок
{
"items": [
{
"exchange_request_id": "r_8c1d2e3f4a",
"number": "2609-412",
"status": "active",
"exchange": "new",
"test": false,
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:42:11.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "today",
"deadline_name": "сегодня",
"priority": "fast",
"priority_name": "быстрее",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001"
},
"buyer": {
"ref": "П-7F3A9C",
"city": "Санкт-Петербург"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Машина на линии, звонить [скрыто]",
"items": [
{
"item_id": "i_1a2b3c4d5e",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"open": true
},
{
"item_id": "i_2b3c4d5e6f",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"open": true
}
],
"max_proposals": 5,
"competition": {
"proposals": 3,
"sellers": 3,
"limit": 5,
"slots_left": 2,
"my_rank": 2,
"my_value_kop": 452900,
"basis": "accept_plan"
},
"my_proposals": [
{
"proposal_id": "o_5f6a7b8c9d",
"number": "ТТ-014-00131",
"status": "sent",
"value_kop": 452900
}
],
"current_proposal_id": "o_5f6a7b8c9d",
"can_propose": true,
"cannot_propose_reason": null,
"replace_available_at": null,
"unread_messages": 1,
"change_seq": 184300
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/seller/exchange/requests?since_seq=0&limit=100" -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/seller/exchange/requests/{id}
Заявка набора продавца в любом статусе
Право: exchangeЛимит: default
Заявки нет, она другого окружения или не входит в набор — 404 (недоступное не отличается от несуществующего).
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 200 Заявка
{
"exchange_request_id": "r_8c1d2e3f4a",
"number": "2609-412",
"status": "active",
"exchange": "new",
"test": false,
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:42:11.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "today",
"deadline_name": "сегодня",
"priority": "fast",
"priority_name": "быстрее",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001"
},
"buyer": {
"ref": "П-7F3A9C",
"city": "Санкт-Петербург"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Машина на линии, звонить [скрыто]",
"items": [
{
"item_id": "i_1a2b3c4d5e",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"open": true
},
{
"item_id": "i_2b3c4d5e6f",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"open": true
}
],
"max_proposals": 5,
"competition": {
"proposals": 3,
"sellers": 3,
"limit": 5,
"slots_left": 2,
"my_rank": 2,
"my_value_kop": 452900,
"basis": "accept_plan"
},
"my_proposals": [
{
"proposal_id": "o_5f6a7b8c9d",
"number": "ТТ-014-00131",
"status": "sent",
"value_kop": 452900
}
],
"current_proposal_id": "o_5f6a7b8c9d",
"can_propose": true,
"cannot_propose_reason": null,
"replace_available_at": null,
"unread_messages": 1,
"change_seq": 184300
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/seller/exchange/requests/r_8c1d2e3f4a -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/seller/exchange/requests/{id}/proposals
Отправить КП покупателю (mode send) или сохранить черновик (mode draft)
Право: exchangeIdempotency-Key: обязателенЛимит: default
Те же проверки, что у кнопок кабинета: доступ к заявке, срок, лимит КП (считает продавцов), строки по позициям заявки, строка по выкупленной позиции — 409 ALREADY_BOUGHT, контакты в тексте — 422 CONTACTS_FORBIDDEN. Повторная отправка заменяет КП sent (superseded); частично купленное КП живёт рядом. Замена через API — не чаще раза в 5 минут и не больше 10 на заявку (409 PROPOSAL_REPLACE_LIMIT). Юридические ворота: боевой ключ компании без принятых документов — 403 LEGAL_NEEDS.
Предел замен (M.10.2) считает повторные отправки КП компании по заявке: замену действующего КП (superseded) и отправку после снятия своего отправленного КП (withdrawn). Снятые черновики, которые не отправлялись, не считаются. Тексты КП (комментарий, строки, путь ссылки на фото) не должны содержать контактов и сведений о продавце: реквизитов, названия, адреса компании и складов, фамилий сотрудников — 422 CONTACTS_FORBIDDEN с kind в details.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
mode | нет | string: send | draft | |
replaces_proposal_id | нет | string | Защита от гонок: должен совпасть с current_proposal_id заявки |
comment | нет | string | |
lines | да | массив object | Не больше 20 вариантов на позицию |
{
"mode": "send",
"comment": "Отгрузка со склада СПб в день оплаты",
"lines": [
{
"item_id": "i_1a2b3c4d5e",
"brand": "MANN",
"article": "W 950/26",
"oem": "51.05504-0108",
"price_kop": 123450,
"qty": 2,
"vat": "vat22",
"term": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"external_id": "A-778"
},
{
"item_id": "i_1a2b3c4d5e",
"brand": "FILTRON",
"article": "OP 592/7",
"price_kop": 98000,
"qty": 2,
"vat": "vat22",
"term_days": 2
},
{
"item_id": "i_2b3c4d5e6f",
"brand": "Contitech",
"article": "6PK1873",
"price_kop": 210000,
"qty": 1,
"vat": "vat22",
"term": "in_stock",
"photo_url": "https://disk.yandex.ru/i/Ab3dE6fG"
}
]
}
Ответ 201 КП продавца
{
"proposal_id": "o_6a7b8c9d0e",
"number": "ТТ-014-00132",
"exchange_request_id": "r_8c1d2e3f4a",
"status": "sent",
"test": false,
"via": "api",
"replaces_proposal_id": null,
"created_at": "2026-09-22T07:45:02.000Z",
"updated_at": "2026-09-22T07:45:02.000Z",
"comment": "Отгрузка со склада СПб в день оплаты",
"value_kop": 406000,
"lines_sum_kop": 652900,
"coverage": {
"items_open": 2,
"items_offered": 2,
"items_full": 2
},
"lines": [
{
"proposal_line_id": "ol_91a2b3c4d5",
"item_id": "i_1a2b3c4d5e",
"brand": "MANN",
"article": "W 950/26",
"oem": "51.05504-0108",
"price_kop": 123450,
"qty": 2,
"sum_kop": 246900,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"photo_url": null,
"external_id": "A-778",
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_92b3c4d5e6",
"item_id": "i_1a2b3c4d5e",
"brand": "FILTRON",
"article": "OP 592/7",
"oem": "",
"price_kop": 98000,
"qty": 2,
"sum_kop": 196000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "days_2_3",
"term_name": "2-3 дня",
"term_days": 2,
"availability": "on_order",
"stock": 0,
"comment": "",
"photo_url": null,
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_93c4d5e6f7",
"item_id": "i_2b3c4d5e6f",
"brand": "Contitech",
"article": "6PK1873",
"oem": "",
"price_kop": 210000,
"qty": 1,
"sum_kop": 210000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 0,
"comment": "",
"photo_url": "https://disk.yandex.ru/i/Ab3dE6fG",
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 1,
"counter_price_kop": null
}
],
"orders": [],
"actions": [
"withdraw"
],
"counter": null,
"competition": {
"proposals": 3,
"sellers": 3,
"limit": 5,
"slots_left": 2,
"my_rank": 1,
"my_value_kop": 406000,
"basis": "accept_plan"
},
"change_seq": 184311
}
Ошибки: 400, 401, 403, 404, 409, 413, 415, 422, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/exchange/requests/r_8c1d2e3f4a/proposals -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: kp-2609-412-1" -d '{"comment":"Отгрузка в день оплаты","lines":[{"item_id":"i_1a2b3c4d5e","brand":"MANN","article":"W 950/26","price_kop":123450,"qty":2,"vat":"vat22","term":"in_stock","external_id":"A-778"},{"item_id":"i_2b3c4d5e6f","brand":"Contitech","article":"6PK1873","price_kop":210000,"qty":1,"photo_url":"https://disk.yandex.ru/i/Ab3dE6fG"}]}'
GET/api/v1/seller/exchange/proposals
КП компании в любом статусе, включая черновики и КП из кабинета (via cabinet)
Право: exchangeЛимит: default
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
exchange_request_id | query | нет | string | |
status | query | нет | string | Через запятую, по умолчанию все |
since_seq | query | нет | integer | Объекты с change_seq строго больше; сортировка change_seq ASC. Опрос без потерь: сохранить change_seq последнего элемента. |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Страница КП (без competition)
{
"items": [
{
"proposal_id": "o_6a7b8c9d0e",
"number": "ТТ-014-00132",
"exchange_request_id": "r_8c1d2e3f4a",
"status": "sent",
"test": false,
"via": "api",
"replaces_proposal_id": null,
"created_at": "2026-09-22T07:45:02.000Z",
"updated_at": "2026-09-22T07:45:02.000Z",
"comment": "Отгрузка со склада СПб в день оплаты",
"value_kop": 406000,
"lines_sum_kop": 652900,
"coverage": {
"items_open": 2,
"items_offered": 2,
"items_full": 2
},
"lines": [
{
"proposal_line_id": "ol_91a2b3c4d5",
"item_id": "i_1a2b3c4d5e",
"brand": "MANN",
"article": "W 950/26",
"oem": "51.05504-0108",
"price_kop": 123450,
"qty": 2,
"sum_kop": 246900,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"photo_url": null,
"external_id": "A-778",
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_92b3c4d5e6",
"item_id": "i_1a2b3c4d5e",
"brand": "FILTRON",
"article": "OP 592/7",
"oem": "",
"price_kop": 98000,
"qty": 2,
"sum_kop": 196000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "days_2_3",
"term_name": "2-3 дня",
"term_days": 2,
"availability": "on_order",
"stock": 0,
"comment": "",
"photo_url": null,
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_93c4d5e6f7",
"item_id": "i_2b3c4d5e6f",
"brand": "Contitech",
"article": "6PK1873",
"oem": "",
"price_kop": 210000,
"qty": 1,
"sum_kop": 210000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 0,
"comment": "",
"photo_url": "https://disk.yandex.ru/i/Ab3dE6fG",
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 1,
"counter_price_kop": null
}
],
"orders": [],
"actions": [
"withdraw"
],
"counter": null,
"change_seq": 184311
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/seller/exchange/proposals?since_seq=0" -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/seller/exchange/proposals/{id}
КП компании
Право: exchangeЛимит: default
Чужое КП и КП другого окружения — 404.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 200 КП
{
"proposal_id": "o_6a7b8c9d0e",
"number": "ТТ-014-00132",
"exchange_request_id": "r_8c1d2e3f4a",
"status": "sent",
"test": false,
"via": "api",
"replaces_proposal_id": null,
"created_at": "2026-09-22T07:45:02.000Z",
"updated_at": "2026-09-22T07:45:02.000Z",
"comment": "Отгрузка со склада СПб в день оплаты",
"value_kop": 406000,
"lines_sum_kop": 652900,
"coverage": {
"items_open": 2,
"items_offered": 2,
"items_full": 2
},
"lines": [
{
"proposal_line_id": "ol_91a2b3c4d5",
"item_id": "i_1a2b3c4d5e",
"brand": "MANN",
"article": "W 950/26",
"oem": "51.05504-0108",
"price_kop": 123450,
"qty": 2,
"sum_kop": 246900,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"photo_url": null,
"external_id": "A-778",
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_92b3c4d5e6",
"item_id": "i_1a2b3c4d5e",
"brand": "FILTRON",
"article": "OP 592/7",
"oem": "",
"price_kop": 98000,
"qty": 2,
"sum_kop": 196000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "days_2_3",
"term_name": "2-3 дня",
"term_days": 2,
"availability": "on_order",
"stock": 0,
"comment": "",
"photo_url": null,
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_93c4d5e6f7",
"item_id": "i_2b3c4d5e6f",
"brand": "Contitech",
"article": "6PK1873",
"oem": "",
"price_kop": 210000,
"qty": 1,
"sum_kop": 210000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 0,
"comment": "",
"photo_url": "https://disk.yandex.ru/i/Ab3dE6fG",
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 1,
"counter_price_kop": null
}
],
"orders": [],
"actions": [
"withdraw"
],
"counter": null,
"competition": {
"proposals": 3,
"sellers": 3,
"limit": 5,
"slots_left": 2,
"my_rank": 1,
"my_value_kop": 406000,
"basis": "accept_plan"
},
"change_seq": 184311
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/seller/exchange/proposals/o_6a7b8c9d0e -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/seller/exchange/proposals/{id}/withdraw
Снять КП
Право: exchangeIdempotency-Key: рекомендуетсяЛимит: default
Снять можно черновик и КП sent, по которому ничего не куплено: купленное — 409 INVALID_STATE, заменённое или закрытое — 409 PROPOSAL_NOT_ACTIVE. Уже снятое — 200 и Idempotent-Replayed: state. Юридические ворота не действуют.
Снятие отправленного КП не обнуляет предел замен API (M.10.2): следующая отправка по этой заявке считается заменой — не раньше replace_min_sec после прошлой отправки и не больше replaces_per_request раз (409 PROPOSAL_REPLACE_LIMIT; в заявке can_propose: false, cannot_propose_reason: replace_limit, replace_available_at). Снятый черновик, который ни разу не отправлялся, заменой не считается.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Ответ 200 КП со статусом withdrawn
{
"proposal_id": "o_6a7b8c9d0e",
"number": "ТТ-014-00132",
"exchange_request_id": "r_8c1d2e3f4a",
"status": "withdrawn",
"test": false,
"via": "api",
"replaces_proposal_id": null,
"created_at": "2026-09-22T07:45:02.000Z",
"updated_at": "2026-09-22T07:45:02.000Z",
"comment": "Отгрузка со склада СПб в день оплаты",
"value_kop": 406000,
"lines_sum_kop": 652900,
"coverage": {
"items_open": 2,
"items_offered": 2,
"items_full": 2
},
"lines": [
{
"proposal_line_id": "ol_91a2b3c4d5",
"item_id": "i_1a2b3c4d5e",
"brand": "MANN",
"article": "W 950/26",
"oem": "51.05504-0108",
"price_kop": 123450,
"qty": 2,
"sum_kop": 246900,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"photo_url": null,
"external_id": "A-778",
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_92b3c4d5e6",
"item_id": "i_1a2b3c4d5e",
"brand": "FILTRON",
"article": "OP 592/7",
"oem": "",
"price_kop": 98000,
"qty": 2,
"sum_kop": 196000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "days_2_3",
"term_name": "2-3 дня",
"term_days": 2,
"availability": "on_order",
"stock": 0,
"comment": "",
"photo_url": null,
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
},
{
"proposal_line_id": "ol_93c4d5e6f7",
"item_id": "i_2b3c4d5e6f",
"brand": "Contitech",
"article": "6PK1873",
"oem": "",
"price_kop": 210000,
"qty": 1,
"sum_kop": 210000,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 0,
"comment": "",
"photo_url": "https://disk.yandex.ru/i/Ab3dE6fG",
"external_id": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 1,
"counter_price_kop": null
}
],
"orders": [],
"actions": [],
"counter": null,
"competition": {
"proposals": 3,
"sellers": 3,
"limit": 5,
"slots_left": 2,
"my_rank": 1,
"my_value_kop": 406000,
"basis": "accept_plan"
},
"change_seq": 184311
}
Ошибки: 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/seller/exchange/proposals/o_6a7b8c9d0e/withdraw -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{}'
Покупатель
GET/api/v1/buyer/search
Поиск предложений: бренды по артикулу или предложения
Право: не нужноЛимит: search
Лимит search на ключ и на компанию. Test-ключ: 0,2 лимита и limit ≤ 20. Без article/q — листинг глубиной до 500 предложений (warnings: LISTING_DEPTH_LIMIT). Склад — только город и обезличенный warehouse_code.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
q | query | нет | string | |
article | query | нет | string | |
brand | query | нет | string | |
brand_id | query | нет | string | Один или несколько id через запятую |
analogs | query | нет | integer: 0 | 1 | |
mode | query | нет | string: auto | brands | offers | auto — артикул без бренда: список брендов (mode: brands), если брендов у артикула больше одного; при одном бренде сразу предложения (mode: offers). brands — список брендов при любом их числе. offers — сразу предложения. Второй шаг — ?article=<article_norm>&brand_id=<brand_id> пары из списка: предложения искомого товара (группа requested), затем аналоги (oem, analog), в том числе присланные поставщиками площадки |
similar | query | нет | integer: 0 | 1 | 1 — в ответе mode: brands ещё похожие номера (similar): номера, которые начинаются с введённого (после нормализации), до 30 пар «бренд + номер». С mode=auto список приходит и при одном бренде, если похожие номера есть. Без параметра ответ прежний |
price_min_kop | query | нет | integer | |
price_max_kop | query | нет | integer | |
term_max | query | нет | integer | |
city_id | query | нет | string | id через запятую |
city | query | нет | string | |
country | query | нет | string | |
seller | query | нет | string | Код продавца ТТ-xxx |
in_stock | query | нет | integer: 0 | 1 | |
condition | query | нет | string: new | used | |
fresh | query | нет | integer: 0 | 1 | |
sort | query | нет | string | Значения: relevance, price, price_desc, term, updated |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Бренды или предложения
{
"mode": "offers",
"query": {
"brand_id": 17,
"article_norm": "01089",
"analogs": true
},
"total": 1,
"total_exact": true,
"groups": [
{
"kind": "requested",
"brand_id": 17,
"brand": "FEBI BILSTEIN",
"article": "01-089",
"article_norm": "01089",
"offers": [
{
"offer_key": "eyJvIjoicF8zYzFmOWUyYTdiIiwicCI6MTIzNDUwfQ.m7Q2x9Lh0aV1c3kP",
"name": "Термостат",
"seller_code": "ТТ-014",
"seller_rating": 4.1,
"refusal_pct": 6.5,
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"country": "RU",
"warehouse_code": "ТТ-014-1",
"price_kop": 123450,
"currency": "RUB",
"qty": 10,
"multiplicity": 1,
"min_order": 1,
"term_days_min": 1,
"term_days_max": 3,
"probability": 95,
"condition": "new",
"returnable": true,
"vat": "НДС 22%",
"availability": "in_stock",
"stale": false,
"price_age_hours": 3,
"photo": null,
"updated_at": "2026-09-15T08:00:00.000Z"
}
]
}
],
"next_cursor": null,
"has_more": false,
"warnings": []
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/buyer/search?article=01-089&brand_id=17" -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/buyer/quotes
Перепроверка до 50 offer_key
Право: не нужноЛимит: search
Перепроверка до 50 offer_key прямо сейчас: цена, доступный остаток, срок. Свежий offer_key выдаётся для ok и price_changed. Кратность и минимальная партия отдаются для сведения: заказ они не блокируют (M.6 п.4).
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
items | да | массив object | |
{
"items": [
{
"offer_key": "eyJvIjoicF8zYzFmOWUyYTdiIiwicCI6MTIzNDUwfQ.m7Q2x9Lh0aV1c3kP",
"qty": 2
}
]
}
Ответ 200 Актуальные условия
{
"items": [
{
"offer_key": "eyJvIjoicF8zYzFmOWUyYTdiIiwicCI6MTIzNDUwfQ.m7Q2x9Lh0aV1c3kPN",
"requested_offer_key": "eyJvIjoicF8zYzFmOWUyYTdiIiwicCI6MTIzNDUwfQ.m7Q2x9Lh0aV1c3kP",
"status": "ok",
"price_kop": 123450,
"qty_available": 10,
"qty_requested": 2,
"multiplicity": 1,
"min_order": 1,
"term_days_min": 1,
"term_days_max": 3,
"availability": "in_stock",
"expires_at": "2026-09-15T09:42:00.000Z"
}
]
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/buyer/quotes -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"items":[{"offer_key":"<offer_key>","qty":2}]}'
GET/api/v1/buyer/orders
Заказы покупателя: по ids, reference или since
Право: orders | exchangeЛимит: default
Заказы компании своего окружения: из магазина (source shop) и из КП биржи (source exchange, поле exchange). Ключ только с правом exchange видит лишь заказы из КП. Синхронизация — since_seq.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
ids | query | нет | string | До 50 id через запятую |
reference | query | нет | string | |
source | query | нет | string: shop | exchange | Источник заказа |
since_seq | query | нет | integer | Объекты с change_seq строго больше; сортировка change_seq ASC. Опрос без потерь: сохранить change_seq последнего элемента. |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
status | query | нет | string | |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Страница
{
"items": [
{
"order_id": "ord_6f2b3c4d5e",
"number": "2609-1190",
"status": "awaiting_confirmation",
"status_name": "Ожидает подтверждения",
"test": false,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"source": "shop",
"exchange": null,
"requires_confirmation": true,
"seller": {
"code": "ТТ-014",
"city": "Санкт-Петербург",
"rating": 4.1
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "счёт",
"comment": "",
"currency": "RUB",
"total_kop": 246900,
"delivery_kop": 0,
"accounting_total_rub": 2469,
"reference": "ТК-Север-4471",
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"oem": "",
"name": "Термостат",
"qty": 2,
"confirmed_qty": null,
"shipped_qty": 0,
"price_kop": 123450,
"sum_kop": 246900,
"status": "awaiting_confirmation",
"reject_reason_code": null,
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": null,
"warehouse": "СПб-1",
"plate": "",
"exchange": null
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
},
"change_seq": 184230
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/buyer/orders?since_seq=184000" -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/buyer/orders
Заказ по offer_key (делится по продавцам)
Право: ordersIdempotency-Key: обязателенЛимит: bulk, тестовым ключом — default
Только организация (у автовладельца — 403 FORBIDDEN_ROLE); боевому ключу компании без принятых документов площадки — 403 LEGAL_NEEDS. Правила — как у корзины сайта; кратность и минимальная партия не проверяются. Test-ключ: заказ test = true на служебного продавца (реальный продавец его не видит), робот песочницы подтверждает строки, строку с qty = 13 отклоняет out_of_stock и через Δ отгружает подтверждённые. Лимит: боевой ключ — класс bulk, тестовый — класс default (x-rate-limit-class-test), чтобы тестовые заказы подряд не упирались в 429.
Истёкший offer_key (старше quoteTtlMinutes) — 409 OFFER_KEY_EXPIRED: повторите поиск (GET /buyer/search), quotes истёкший ключ не продлевает.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
reference | нет | string | |
items | да | массив object | |
delivery | нет | object | |
payment | нет | string | Код справочника payments (GET /dictionary/exchange) или подпись, как у методов биржи. Без поля — с баланса, если тип компании её допускает (balancePaymentTypes), иначе счёт. Явная оплата с баланса у другого типа — 422 BUSINESS_RULE Значения: balance, invoice, card_on_delivery, с баланса, счёт, картой при получении |
accept_terms | да | boolean: true | Ознакомление с условиями возврата; обязательно true, иначе 422 BUSINESS_RULE (как у корзины сайта) |
price_policy | нет | string: strict | accept_lower | |
comment | нет | string | Комментарий к заказу, как в корзине сайта: продавец видит его в заказе |
{
"reference": "ТК-Север-4471",
"items": [
{
"offer_key": "eyJvIjoi…",
"qty": 2
}
],
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "счёт",
"accept_terms": true,
"price_policy": "strict"
}
Ответ 201 Заказы созданы
{
"reference": "ТК-Север-4471",
"orders": [
{
"order_id": "ord_6f2b",
"number": "2609-1191",
"seller_code": "ТТ-014",
"status": "awaiting_confirmation",
"total_kop": 246900,
"accounting_total_rub": 2469,
"lines": [
{
"line_id": "l_1a2b",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"qty": 2,
"price_kop": 123450,
"status": "awaiting_confirmation"
}
]
}
]
}
Ошибки: 400, 401, 403, 409, 422, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/buyer/orders -H "Authorization: Bearer $K247_TOKEN" -H "Idempotency-Key: TK-4471" -H "Content-Type: application/json" -d '{"reference":"ТК-Север-4471","items":[{"offer_key":"<offer_key>","qty":2}],"payment":"invoice","accept_terms":true}'
GET/api/v1/buyer/orders/{id}
Заказ покупателя
Право: orders | exchangeЛимит: default
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 200 Заказ
{
"order_id": "ord_6f2b3c4d5e",
"number": "2609-1190",
"status": "awaiting_confirmation",
"status_name": "Ожидает подтверждения",
"test": false,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"source": "shop",
"exchange": null,
"requires_confirmation": true,
"seller": {
"code": "ТТ-014",
"city": "Санкт-Петербург",
"rating": 4.1
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "счёт",
"comment": "",
"currency": "RUB",
"total_kop": 246900,
"delivery_kop": 0,
"accounting_total_rub": 2469,
"reference": "ТК-Север-4471",
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"oem": "",
"name": "Термостат",
"qty": 2,
"confirmed_qty": null,
"shipped_qty": 0,
"price_kop": 123450,
"sum_kop": 246900,
"status": "awaiting_confirmation",
"reject_reason_code": null,
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": null,
"warehouse": "СПб-1",
"plate": "",
"exchange": null
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
},
"change_seq": 184230
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/buyer/orders/ord_6f2b3c4d5e -H "Authorization: Bearer $K247_TOKEN"
Биржа: покупатель
GET/api/v1/buyer/exchange/requests
Заявки компании: status, reference, since_seq
Право: exchangeЛимит: default
Заявки компании своего окружения, в том числе созданные сотрудниками в кабинете. Номер изменения заявки растёт и при новом КП — один опрос показывает, где появились КП.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
status | query | нет | string | Список через запятую: active, done, closed, expired или all |
reference | query | нет | string | Свой номер заявки покупателя |
since_seq | query | нет | integer | Объекты с change_seq строго больше; сортировка change_seq ASC. Опрос без потерь: сохранить change_seq последнего элемента. |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Страница
{
"items": [
{
"exchange_request_id": "r_9d2e3f4a5b",
"number": "2609-412",
"status": "active",
"exchange": "new",
"test": false,
"via": "api",
"reference": "ТК-Север-4471",
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:10:00.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "today",
"deadline_name": "сегодня",
"priority": "fast",
"priority_name": "быстрее",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001",
"plate": "А123ВС78"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"pickup_address": "ул. Салова, 27",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Машина на линии",
"max_proposals": 5,
"items": [
{
"item_id": "i_3c4d5e6f7a",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"bought_qty": 0,
"need_qty": 2,
"open": true
},
{
"item_id": "i_4d5e6f7a8b",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"bought_qty": 0,
"need_qty": 1,
"open": true
}
],
"proposals_summary": {
"active": 0,
"sellers": 0,
"total": 0,
"best_value_kop": null
},
"unread_messages": 0,
"change_seq": 184290
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/buyer/exchange/requests?status=active&limit=50" -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/buyer/exchange/requests
Создать заявку на бирже
Право: exchangeIdempotency-Key: обязателенЛимит: default
Только организация (у автовладельца — 403 FORBIDDEN_ROLE). Боевому ключу компании без принятых документов площадки — 403 LEGAL_NEEDS. Пределы: не больше exchangeApiRequestsPerHour заявок в час и exchangeApiActiveMax действующих заявок из программы на компанию; тестовым ключом — пятая часть часового предела, не больше 20 действующих и 100 за сутки (429 RATE_LIMITED).
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
reference | нет | string | Свой номер заявки покупателя: по нему ищет GET …?reference= |
exchange | нет | string: new | used | all | |
vehicle | да | object | |
items | да | массив object | |
deadline | нет | string: today | days_2_3 | week | |
priority | нет | string: fast | cheap | any | |
max_proposals | нет | integer | |
delivery | нет | string: main | pickup | express | |
pickup | нет | string: delivery | pickup | |
pickup_address | нет | string | Адрес получения (адрес доставки по этой заявке); без поля — адрес компании. Продавцу не отдаётся ни в API, ни в кабинете: адрес он видит только в заказе. При «Принять» без delivery.address становится адресом доставки заказа (кроме самовывоза) |
payment | нет | string | По умолчанию — balance разрешённым типам компаний, иначе invoice Значения: balance, invoice, card_on_delivery |
city | нет | string | |
region | нет | string | Регион из справочника GET /dictionary/exchange |
comment | нет | string | |
sandbox | нет | object | Только тестовый ключ: сценарий роботов-продавцов песочницы (M.9.1) |
{
"reference": "ТК-Север-4471",
"exchange": "new",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001",
"plate": "А123ВС78"
},
"items": [
{
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN"
},
{
"name": "Ремень приводной",
"qty": 1
}
],
"deadline": "today",
"priority": "fast",
"max_proposals": 5,
"delivery": "main",
"pickup": "delivery",
"pickup_address": "ул. Салова, 27",
"payment": "invoice",
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"comment": "Машина на линии"
}
Ответ 201 Заявка создана
{
"exchange_request_id": "r_9d2e3f4a5b",
"number": "2609-412",
"status": "active",
"exchange": "new",
"test": false,
"via": "api",
"reference": "ТК-Север-4471",
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:10:00.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "today",
"deadline_name": "сегодня",
"priority": "fast",
"priority_name": "быстрее",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001",
"plate": "А123ВС78"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"pickup_address": "ул. Салова, 27",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Машина на линии",
"max_proposals": 5,
"items": [
{
"item_id": "i_3c4d5e6f7a",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"bought_qty": 0,
"need_qty": 2,
"open": true
},
{
"item_id": "i_4d5e6f7a8b",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"bought_qty": 0,
"need_qty": 1,
"open": true
}
],
"proposals_summary": {
"active": 0,
"sellers": 0,
"total": 0,
"best_value_kop": null
},
"unread_messages": 0,
"change_seq": 184290
}
Ошибки: 400, 401, 403, 409, 422, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/buyer/exchange/requests -H "Authorization: Bearer $K247_TOKEN" -H "Idempotency-Key: REQ-4471" -H "Content-Type: application/json" -d '{"reference":"ТК-Север-4471","vehicle":{"vgroup":"truck","market":"eu","brand":"MAN","model":"TGX"},"items":[{"name":"Фильтр масляный","qty":2},{"name":"Ремень приводной","qty":1}],"deadline":"week","payment":"invoice"}'
GET/api/v1/buyer/exchange/requests/{id}
Заявка компании
Право: exchangeЛимит: default
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 200 Заявка
{
"exchange_request_id": "r_9d2e3f4a5b",
"number": "2609-412",
"status": "active",
"exchange": "new",
"test": false,
"via": "api",
"reference": "ТК-Север-4471",
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:10:00.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "today",
"deadline_name": "сегодня",
"priority": "fast",
"priority_name": "быстрее",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001",
"plate": "А123ВС78"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"pickup_address": "ул. Салова, 27",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Машина на линии",
"max_proposals": 5,
"items": [
{
"item_id": "i_3c4d5e6f7a",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"bought_qty": 0,
"need_qty": 2,
"open": true
},
{
"item_id": "i_4d5e6f7a8b",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"bought_qty": 0,
"need_qty": 1,
"open": true
}
],
"proposals_summary": {
"active": 0,
"sellers": 0,
"total": 0,
"best_value_kop": null
},
"unread_messages": 0,
"change_seq": 184290
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/buyer/exchange/requests/r_9d2e3f4a5b -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/buyer/exchange/requests/{id}/update
Изменить заявку
Право: exchangeIdempotency-Key: рекомендуетсяЛимит: default
Юридические ворота не действуют: правка — не новое обязательство. Купленную позицию нельзя удалить, переименовать или уменьшить ниже купленного (409 INVALID_STATE). Переименование или удаление позиции закрывает строки КП по ней. Биржу (exchange) можно расширить всегда; сузить — нельзя, пока продавцы, которые потеряют доступ к заявке, держат действующие КП (409 INVALID_STATE, details[0].field = exchange).
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
vehicle | нет | object | |
items | нет | массив object | |
exchange | нет | string: new | used | all | Биржа заявки. Расширить (new или used → all) можно всегда: продавцам, которым заявка стала видна, уходит exchange.request.created. Сузить (all → new или used, new ↔ used) нельзя, пока у продавцов, которые потеряют доступ к заявке, есть действующие КП (sent, partial): 409 INVALID_STATE, details[0].field = exchange; каких продавцов это касается, ответ не говорит. Черновики таких продавцов закрываются. Номер заявки не меняется. null или "" — 400 |
deadline | нет | string: today | days_2_3 | week | |
priority | нет | string: fast | cheap | any | |
max_proposals | нет | integer | |
delivery | нет | string: main | pickup | express | |
pickup | нет | string: delivery | pickup | |
pickup_address | нет | string | |
payment | нет | string | По умолчанию — balance разрешённым типам компаний, иначе invoice Значения: balance, invoice, card_on_delivery |
city | нет | string | |
region | нет | string | Регион из справочника GET /dictionary/exchange |
comment | нет | string | |
{
"deadline": "week",
"max_proposals": 5,
"comment": "Машина на линии, нужен счёт на оплату"
}
Ответ 200 Заявка после правки
{
"exchange_request_id": "r_9d2e3f4a5b",
"number": "2609-412",
"status": "active",
"exchange": "new",
"test": false,
"via": "api",
"reference": "ТК-Север-4471",
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:10:00.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "today",
"deadline_name": "сегодня",
"priority": "fast",
"priority_name": "быстрее",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001",
"plate": "А123ВС78"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"pickup_address": "ул. Салова, 27",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Машина на линии",
"max_proposals": 5,
"items": [
{
"item_id": "i_3c4d5e6f7a",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"bought_qty": 0,
"need_qty": 2,
"open": true
},
{
"item_id": "i_4d5e6f7a8b",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"bought_qty": 0,
"need_qty": 1,
"open": true
}
],
"proposals_summary": {
"active": 0,
"sellers": 0,
"total": 0,
"best_value_kop": null
},
"unread_messages": 0,
"change_seq": 184290
}
Ошибки: 400, 401, 403, 404, 409, 422, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/buyer/exchange/requests/r_9d2e3f4a5b/update -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -d '{"deadline":"week","max_proposals":5}'
POST/api/v1/buyer/exchange/requests/{id}/close
Снять заявку
Право: exchangeIdempotency-Key: рекомендуетсяЛимит: default
Заявка → closed, её КП sent и draft → closed, строки корзин по заявке уходят. Уже снятая заявка — 200 и Idempotent-Replayed: state; выкупленную или истёкшую закрыть нельзя (409 REQUEST_CLOSED).
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Ответ 200 Заявка снята
{
"exchange_request_id": "r_9d2e3f4a5b",
"number": "2609-412",
"status": "closed",
"exchange": "new",
"test": false,
"via": "api",
"reference": "ТК-Север-4471",
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:10:00.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "today",
"deadline_name": "сегодня",
"priority": "fast",
"priority_name": "быстрее",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001",
"plate": "А123ВС78"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"pickup_address": "ул. Салова, 27",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Машина на линии",
"max_proposals": 5,
"items": [
{
"item_id": "i_3c4d5e6f7a",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"bought_qty": 0,
"need_qty": 2,
"open": true
},
{
"item_id": "i_4d5e6f7a8b",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"bought_qty": 0,
"need_qty": 1,
"open": true
}
],
"proposals_summary": {
"active": 0,
"sellers": 0,
"total": 0,
"best_value_kop": null
},
"unread_messages": 0,
"change_seq": 184290
}
Ошибки: 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/buyer/exchange/requests/r_9d2e3f4a5b/close -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/buyer/exchange/proposals
КП по заявкам компании
Право: exchangeЛимит: default
Черновики продавцов сюда не попадают. Без status и since_seq — действующие КП (sent, partial, accepted); с since_seq — все статусы. sort=value — по стоимости КП, только вместе с exchange_request_id и без курсора.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
exchange_request_id | query | нет | string | |
status | query | нет | string | Список через запятую или all |
sort | query | нет | string: value | value — все КП заявки по стоимости (value_kop) по возрастанию, в ответе первые limit; курсора нет, has_more: true — КП больше limit. Только с exchange_request_id, без since_seq, since и cursor |
since_seq | query | нет | integer | Объекты с change_seq строго больше; сортировка change_seq ASC. Опрос без потерь: сохранить change_seq последнего элемента. |
since | query | нет | string | Объекты с updated_at строго больше (для людей и отладки; для синхронизации используйте since_seq — время ставится до коммита и не гарантирует полноты) |
limit | query | нет | integer | |
cursor | query | нет | string | Непрозрачный курсор из next_cursor |
Ответ 200 Страница
{
"items": [
{
"proposal_id": "o_7b8c9d0e1f",
"number": "ТТ-014-00132",
"exchange_request_id": "r_9d2e3f4a5b",
"status": "sent",
"test": false,
"replaces_proposal_id": null,
"seller": {
"code": "ТТ-014",
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"rating": 4.8,
"refusal_pct": null
},
"created_at": "2026-09-22T07:45:02.000Z",
"updated_at": "2026-09-22T07:45:02.000Z",
"comment": "Отгрузка со склада СПб в день оплаты",
"value_kop": 406000,
"lines_sum_kop": 652900,
"coverage": {
"items_open": 2,
"items_offered": 2,
"items_full": 2
},
"lines": [
{
"proposal_line_id": "ol_a1b2c3d4e5",
"item_id": "i_3c4d5e6f7a",
"brand": "MANN",
"article": "",
"oem": "",
"price_kop": 123450,
"qty": 2,
"sum_kop": 246900,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"photo_url": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
}
],
"orders": [],
"actions": [
"accept",
"reject"
],
"counter": null,
"change_seq": 184311
}
],
"next_cursor": null,
"has_more": false
}
Ошибки: 400, 401, 403, 429, 500, 503
Пример curl
curl -s "https://k-247.ru/api/v1/buyer/exchange/proposals?exchange_request_id=r_9d2e3f4a5b&sort=value" -H "Authorization: Bearer $K247_TOKEN"
GET/api/v1/buyer/exchange/proposals/{id}
КП по заявке компании
Право: exchangeЛимит: default
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 200 КП
{
"proposal_id": "o_7b8c9d0e1f",
"number": "ТТ-014-00132",
"exchange_request_id": "r_9d2e3f4a5b",
"status": "sent",
"test": false,
"replaces_proposal_id": null,
"seller": {
"code": "ТТ-014",
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"rating": 4.8,
"refusal_pct": null
},
"created_at": "2026-09-22T07:45:02.000Z",
"updated_at": "2026-09-22T07:45:02.000Z",
"comment": "Отгрузка со склада СПб в день оплаты",
"value_kop": 406000,
"lines_sum_kop": 652900,
"coverage": {
"items_open": 2,
"items_offered": 2,
"items_full": 2
},
"lines": [
{
"proposal_line_id": "ol_a1b2c3d4e5",
"item_id": "i_3c4d5e6f7a",
"brand": "MANN",
"article": "",
"oem": "",
"price_kop": 123450,
"qty": 2,
"sum_kop": 246900,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"photo_url": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
}
],
"orders": [],
"actions": [
"accept",
"reject"
],
"counter": null,
"change_seq": 184311
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/buyer/exchange/proposals/o_7b8c9d0e1f -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/buyer/exchange/proposals/{id}/accept
Принять КП целиком или по строкам
Право: exchangeIdempotency-Key: обязателенЛимит: default
Оформление идёт тем же путём, что корзина сайта: одно «Принять» — один заказ на КП. Без lines берётся план «Принять» (по одному самому дешёвому варианту на позицию в пределах потребности). accept_terms обязателен и в песочнице. Адрес доставки заказа — delivery.address, без него — адрес получения заявки (pickup_address), при самовывозе — пункт самовывоза площадки «самовывоз, <адрес площадки>» (если адрес площадки не указан — адрес компании). Повтор с тем же Idempotency-Key отдаёт те же заказы.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
lines | нет | массив object | Строки этого КП; без поля — план «Принять»: по одному самому дешёвому варианту на каждую открытую позицию |
payment | нет | string | Без поля — способ из заявки Значения: balance, invoice, card_on_delivery |
delivery | нет | object | |
plate | нет | string | |
reference | нет | string | |
accept_terms | да | boolean: true | Согласие с условиями возврата обязательно и в песочнице |
{
"lines": [
{
"proposal_line_id": "ol_a1b2c3d4e5",
"qty": 2
}
],
"payment": "invoice",
"delivery": {
"type": "main",
"address": "ул. Салова, 27"
},
"plate": "А123ВС78",
"reference": "ТК-Север-4471",
"accept_terms": true
}
Ответ 201 Заказ оформлен
{
"exchange_request_id": "r_9d2e3f4a5b",
"proposal_id": "o_7b8c9d0e1f",
"proposal_status": "partial",
"request_status": "active",
"orders": [
{
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1195",
"source": "exchange",
"seller_code": "ТТ-014",
"status": "awaiting_payment",
"total_kop": 246900,
"accounting_total_rub": 2469,
"lines": [
{
"line_id": "l_7c8d9e0f1a",
"proposal_line_id": "ol_a1b2c3d4e5",
"item_id": "i_3c4d5e6f7a",
"brand": "MANN",
"article": "W 950/26",
"oem": "51.05504-0108",
"qty": 2,
"price_kop": 123450,
"status": null
}
]
}
]
}
Ошибки: 400, 401, 403, 404, 409, 422, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/buyer/exchange/proposals/o_7b8c9d0e1f/accept -H "Authorization: Bearer $K247_TOKEN" -H "Idempotency-Key: ACC-4471" -H "Content-Type: application/json" -d '{"lines":[{"proposal_line_id":"ol_a1b2c3d4e5"}],"accept_terms":true}'
POST/api/v1/buyer/exchange/proposals/{id}/reject
Отклонить КП
Право: exchangeIdempotency-Key: рекомендуетсяЛимит: default
Строки pending → rejected, продавец получает уведомление и событие exchange.proposal.rejected. Повтор, когда отклонять уже нечего, — 200 и Idempotent-Replayed: state, без второго уведомления. Тело пустое: причины и встречной цены в v1 нет.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Idempotency-Key | заголовок | нет | string | |
Ответ 200 КП отклонено
{
"proposal_id": "o_8c9d0e1f2a",
"number": "ТТ-014-00132",
"exchange_request_id": "r_9d2e3f4a5b",
"status": "rejected",
"test": false,
"replaces_proposal_id": null,
"seller": {
"code": "ТТ-014",
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"rating": 4.8,
"refusal_pct": null
},
"created_at": "2026-09-22T07:45:02.000Z",
"updated_at": "2026-09-22T07:45:02.000Z",
"comment": "Отгрузка со склада СПб в день оплаты",
"value_kop": 406000,
"lines_sum_kop": 652900,
"coverage": {
"items_open": 2,
"items_offered": 2,
"items_full": 2
},
"lines": [
{
"proposal_line_id": "ol_a1b2c3d4e5",
"item_id": "i_3c4d5e6f7a",
"brand": "MANN",
"article": "",
"oem": "",
"price_kop": 123450,
"qty": 2,
"sum_kop": 246900,
"vat": "vat22",
"vat_name": "НДС 22%",
"term": "in_stock",
"term_name": "В наличии",
"term_days": 0,
"availability": "in_stock",
"stock": 12,
"comment": "оригинальная упаковка",
"photo_url": null,
"status": "pending",
"bought_qty": 0,
"left_qty": 2,
"counter_price_kop": null
}
],
"orders": [],
"actions": [],
"counter": null,
"change_seq": 184311
}
Ошибки: 401, 403, 404, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/buyer/exchange/proposals/o_8c9d0e1f2a/reject -H "Authorization: Bearer $K247_TOKEN"
Вебхуки
GET/api/v1/webhooks
Подписки окружения ключа
Право: offers | orders | exchangeЛимит: default
Подписки компании в окружении ключа (test-ключ видит только тестовые, live — только боевые), без секрета. failures — неудачных доставок подряд, last_delivery — итог последней попытки.
Ответ 200 Подписки
{
"items": [
{
"webhook_id": "wh_4b2a1c0d9e",
"url": "https://erp.shop.ru/k247/hook",
"events": [
"order.created",
"order.line_cancelled",
"import.held"
],
"env": "live",
"enabled": true,
"failures": 0,
"disabled_reason": null,
"description": "1С УТ",
"last_delivery": {
"at": "2026-09-15T09:40:00.000Z",
"status": "delivered",
"code": 200
},
"created_at": "2026-09-15T09:30:00.000Z"
}
]
}
Ошибки: 401, 403, 429, 500, 503
Пример curl
curl -s https://k-247.ru/api/v1/webhooks -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/webhooks
Создать подписку (секрет показывается один раз)
Право: offers | orders | exchangeIdempotency-Key: рекомендуетсяЛимит: default
Подписка на события компании. Окружение подписки = окружение ключа: test-подписка получает события песочницы (тестовые заказы, проверочные загрузки), live — боевые. Проверки: адрес https (порты 80, 443, 8080, 8443, без логина в адресе; имя сервера не должно вести во внутреннюю сеть), события из WebhookEvent или ["*"], не больше webhooksMaxPerEnv (5) подписок на компанию и окружение — иначе 409 LIMIT_EXCEEDED. Секрет подписи whsec_… возвращается один раз: сохраните его, посмотреть снова нельзя (повтор с тем же Idempotency-Key возвращает тот же ответ). О новой подписке узнают сотрудники компании. Продавцу приходят order.* по его заказам, import.* и price.stale по его прайсам; покупателю — order.* по его заказам.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
Idempotency-Key | заголовок | нет | string | |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
url | да | string | |
events | да | массив string | Значения: order.created, order.line_confirmed, order.line_rejected, order.line_cancelled, order.shipped, import.finished, import.held, price.stale, exchange.request.created, exchange.request.updated, exchange.request.closed, exchange.proposal.received, exchange.proposal.replaced, exchange.proposal.withdrawn, exchange.proposal.accepted, exchange.proposal.rejected, *, order.*, import.*, exchange.* |
description | нет | string | |
options | нет | object | Фильтр exchange.request.created (M.8.3): регион заявок (all — ключу с регионом all и подписке из кабинета), группы и рынки техники |
{
"url": "https://erp.shop.ru/k247/hook",
"events": [
"order.created",
"order.line_cancelled",
"import.held"
],
"description": "1С УТ"
}
Ответ 201 Подписка создана
{
"webhook_id": "wh_4b2a1c0d9e",
"url": "https://erp.shop.ru/k247/hook",
"events": [
"order.created",
"order.line_cancelled",
"import.held"
],
"env": "live",
"enabled": true,
"failures": 0,
"disabled_reason": null,
"description": "1С УТ",
"last_delivery": null,
"created_at": "2026-09-15T09:30:00.000Z",
"secret": "whsec_Qm9hY2hlbGxvV2ViaG9va1NlY3JldDI0"
}
Ошибки: 400, 401, 403, 409, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/webhooks -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: hook-erp-1" -d '{"url":"https://erp.shop.ru/k247/hook","events":["order.created","order.line_cancelled","import.held"],"description":"1С УТ"}'
Пример Node.js
const r = await fetch('https://k-247.ru/api/v1/webhooks', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.K247_TOKEN, 'Content-Type': 'application/json', 'Idempotency-Key': 'hook-erp-1' },
body: JSON.stringify({ url: 'https://erp.shop.ru/k247/hook', events: ['order.created', 'order.line_cancelled', 'import.held'], description: '1С УТ' })
});
const hook = await r.json();
// hook.secret показывается один раз — сохраните его в настройках приёмника
console.log(r.status, hook.webhook_id);
DELETE/api/v1/webhooks/{id}
Удалить подписку
Право: offers | orders | exchangeЛимит: default
Удалить подписку окружения ключа. Недоставленные события подписки больше не отправляются. Чужая подписка или подписка другого окружения — 404.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 204 Удалено
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s -X DELETE https://k-247.ru/api/v1/webhooks/wh_4b2a1c0d9e -H "Authorization: Bearer $K247_TOKEN"
POST/api/v1/webhooks/{id}/test
Отправить событие ping
Право: offers | orders | exchangeЛимит: default
Поставить в очередь проверочное событие ping (в data — {"type":"ping","webhook_id":"…"}): оно подписано и доставляется как обычное событие, в том числе выключенной подписке. Итог доставки — в GET /webhooks (last_delivery) и в журнале кабинета. Не больше 10 проверочных событий в час на подписку (выключенную — 3), сверх — 429 RATE_LIMITED с Retry-After. Если прежний ping подписки ещё ждёт доставки, новый не создаётся: прежний отправляется сейчас, в ответе — его event_id.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
id | путь | да | string | |
Ответ 202 Поставлено в очередь
{
"event_id": "evt_01J8Z6A1B2C3"
}
Ошибки: 401, 403, 404, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/webhooks/wh_4b2a1c0d9e/test -H "Authorization: Bearer $K247_TOKEN"
Песочница
POST/api/v1/sandbox/orders
Тестовый заказ на собственный товар (только test-ключ продавца)
Право: ordersIdempotency-Key: обязателенЛимит: default
Только test-ключ продавца (live → 403 FORBIDDEN_ENV). Заказ test=true от служебного покупателя c_sandbox, requires_confirmation; остатки, резервы, деньги и уведомления в общую ленту не затрагиваются. Заказ оформляется на собственную позицию продавца (без lines — на любую свою с остатком), поэтому нужна хотя бы одна опубликованная позиция: test-ключ позиций не создаёт (offers/batch и pricelists — dry_run), загрузите прайс в кабинете (файл, ссылка, почта) или боевым ключом. Позиций нет — 422 BUSINESS_RULE.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
lines | нет | массив object | |
delivery | нет | object | |
{
"lines": [
{
"external_id": "A-779",
"qty": 1
}
],
"delivery": {
"type": "основной",
"address": "Тестовый адрес, 1"
}
}
Ответ 201 Тестовый заказ
{
"order_id": "ord_5e1a2b3c4d",
"number": "2609-1190",
"status": "awaiting_confirmation",
"status_name": "Ожидает подтверждения",
"test": true,
"created_at": "2026-09-15T09:05:00.000Z",
"updated_at": "2026-09-15T09:05:00.000Z",
"change_seq": 184230,
"confirm_deadline_at": "2026-09-15T11:05:00.000Z",
"auto_cancel_at": "2026-09-15T13:05:00.000Z",
"buyer": {
"code": "Покупатель",
"city": "Санкт-Петербург"
},
"delivery": {
"type": "основной",
"address": "ул. Салова, 27",
"express": false
},
"payment": "с баланса",
"comment": "",
"currency": "RUB",
"total_kop": 246900,
"delivery_kop": 0,
"accounting_total_rub": 2469,
"lines": [
{
"line_id": "l_9a8b7c6d5e",
"offer_id": "p_3c1f9e2a7b",
"external_id": "A-778",
"brand": "FEBI BILSTEIN",
"article": "01-089",
"name": "Термостат",
"qty": 2,
"confirmed_qty": null,
"shipped_qty": 0,
"price_kop": 123450,
"sum_kop": 246900,
"status": "awaiting_confirmation",
"reject_reason_code": null,
"term_days_min": 1,
"term_days_max": 3,
"term_days_confirmed": null,
"warehouse": "СПб-1",
"external_order_ref": null,
"plate": ""
}
],
"documents": {
"invoice": "",
"upd": "",
"ksf": ""
}
}
Ошибки: 400, 401, 403, 404, 409, 422, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/sandbox/orders -H "Authorization: Bearer $K247_TOKEN" -H "Idempotency-Key: sbx-1" -H "Content-Type: application/json" -d '{}'
POST/api/v1/sandbox/exchange/requests
Тестовая заявка робота-покупателя для своей компании (только test-ключ продавца)
Право: exchangeIdempotency-Key: обязателенЛимит: default
Live-ключ — 403 FORBIDDEN_ENV. Заявка test = true от служебного покупателя: биржа — по типу продавца, регион и город — продавца, машина MAN TGX 2016. Сценарий робот выполняет через sandboxAutoConfirmSec (30 с) после первого КП: accept — принять КП целиком (test-заказ из КП), accept_partial — купить первую позицию, reject — отклонять КП, close — снять заявку, expire — закрыть по сроку, compete — ответить КП робота ТЕСТ-000 на 10 % дешевле и купить у него. Действующих тестовых заявок — не больше 20 (409 LIMIT_EXCEEDED), за сутки — не больше 100 (429 RATE_LIMITED с Retry-After). Событие exchange.request.created уходит только на test-подписку этой компании.
Параметры
| Имя | Где | Обяз. | Тип | Описание |
|---|
Idempotency-Key | заголовок | да | string | Повтор с тем же телом возвращает сохранённый ответ (заголовок Idempotent-Replayed: true), с другим телом — 409
Ключ хранится idempotencyTtlHours (24 ч). Позже тот же ключ — новый запрос: создаётся новая заявка, КП или заказ, прежний объект не возвращается. Ключ, которым уже выполнена другая операция в пределах срока, — 409 IDEMPOTENCY_CONFLICT. |
Тело запроса application/json
| Поле | Обяз. | Тип | Описание |
|---|
scenario | нет | string | Значения: accept, accept_partial, reject, close, expire, compete |
items | нет | массив object | |
deadline | нет | string: today | days_2_3 | week | |
{
"scenario": "accept",
"items": [
{
"name": "Фильтр масляный",
"qty": 2
},
{
"name": "Ремень приводной",
"qty": 1
}
],
"deadline": "week"
}
Ответ 201 Тестовая заявка в формате GET /seller/exchange/requests/{id}
{
"exchange_request_id": "r_8c1d2e3f4a",
"number": "ТЕСТ-З-2609-7",
"status": "active",
"exchange": "new",
"test": true,
"created_at": "2026-09-22T07:10:00.000Z",
"updated_at": "2026-09-22T07:42:11.000Z",
"revision": 0,
"revised_at": null,
"expires_at": "2026-09-22T20:59:59.000Z",
"deadline": "week",
"deadline_name": "неделя",
"priority": "any",
"priority_name": "все",
"vehicle": {
"vgroup": "truck",
"market": "eu",
"category": "Грузовые / Европа",
"brand": "MAN",
"model": "TGX",
"year": "2016",
"vin": "WMA06XZZ0FP000001"
},
"buyer": {
"ref": "П-7F3A9C",
"city": "Санкт-Петербург"
},
"city": "Санкт-Петербург",
"region": "Санкт-Петербург",
"delivery": "main",
"delivery_name": "основной",
"pickup": "delivery",
"pickup_name": "доставка",
"payment": "invoice",
"payment_name": "счёт",
"comment": "Тестовая заявка робота-покупателя песочницы",
"items": [
{
"item_id": "i_1a2b3c4d5e",
"position": 1,
"name": "Фильтр масляный",
"qty": 2,
"comment": "по VIN",
"open": true
},
{
"item_id": "i_2b3c4d5e6f",
"position": 2,
"name": "Ремень приводной",
"qty": 1,
"comment": "",
"open": true
}
],
"max_proposals": 0,
"competition": {
"proposals": 0,
"sellers": 0,
"limit": 0,
"slots_left": null,
"my_rank": 0,
"my_value_kop": 0,
"basis": "accept_plan"
},
"my_proposals": [],
"current_proposal_id": null,
"can_propose": true,
"cannot_propose_reason": null,
"replace_available_at": null,
"unread_messages": 0,
"change_seq": 184300
}
Ошибки: 400, 401, 403, 409, 422, 429, 500, 503
Пример curl
curl -s -X POST https://k-247.ru/api/v1/sandbox/exchange/requests -H "Authorization: Bearer $K247_TOKEN" -H "Content-Type: application/json" -H "Idempotency-Key: sbx-ex-1" -d '{"scenario":"accept"}'
Изменения
Ломающие изменения — только в новой версии /api/v2 с уведомлением за 30 дней.
- 1.7.02026-10-16
- Поиск покупателя (GET /buyer/search) в два шага: сначала «что нашлось по номеру», затем предложения выбранной пары и аналоги.
- Ответ mode: brands: у каждой пары новые поля article (номер, как его пишут продавцы) и article_norm (нормализованный номер пары); второй шаг — ?article=&brand_id=.
- Новый необязательный параметр similar=1: в ответе mode: brands ещё список similar — похожие номера (начинаются с введённого после нормализации), до 30 пар; больше — предупреждение SIMILAR_TRUNCATED в warnings. С mode=auto список приходит и при одном бренде, если похожие номера есть.
- Второй шаг (brand_id одного бренда): в группах analog — и аналоги, которые прислали поставщики площадки (продавец — К-247, город склада, срок; поставщик не называется).
- Прежние запросы и ответы не меняются: без similar ответы те же, новые поля — дополнительные.
- 1.6.02026-10-13
- Гарантия на позицию: два новых необязательных поля — warranty_months (срок в месяцах, 1–120; null — срок не указан, действует гарантия по условиям площадки) и warranty_manufacturer (true — гарантия изготовителя детали, false — гарантия продавца). Свободного текста гарантии нет.
- Продавец: поля принимает POST /seller/offers/batch (null в warranty_months снимает срок, поля нет — срок не меняется) и отдаёт GET /seller/offers; правка гарантии меняет change_seq позиции. В файлах прайса — колонки «Гарантия» (число месяцев или текст: «24 мес.», «2 года») и «Гарантия изготовителя» (да / нет); поля есть в GET /dictionary/fields. Неверное значение в offers/batch — код строки WARRANTY_INVALID, строка не записывается. В файле прайса гарантия — необязательное сведение: неразобранное значение («есть», «нет», «0», «14 дней») строку не отклоняет — она принимается без срока гарантии с предупреждением WARRANTY_IGNORED в errors отчёта импорта, число таких строк — counters.warranty_ignored (оба кода — новые в перечне BatchRowCode).
- Биржа: поля принимают строки КП (POST /seller/exchange/requests/{id}/proposals — отправка и черновик) и отдают строки КП продавцу и покупателю (lines[].warranty_months, lines[].warranty_manufacturer).
- Покупатель: поля есть в предложениях GET /buyer/search и в заменах отказанной позиции заказа.
- Строки заказа продавца и покупателя (lines[]) несут warranty_months и warranty_manufacturer — гарантию позиции на момент заказа.
- Прежние запросы и ответы не меняются: поля необязательные, у позиций без срока — null и false.
- 1.5.22026-10-12
- Справочник биржи (GET /dictionary/exchange): max_proposals.cabinet — [0] (было [0, 3, 5, 10]). Форма заявки в кабинете лимит числа КП больше не задаёт — новая заявка кабинета идёт без лимита. API принимает max_proposals 0–50, как раньше; лимит прежних заявок сохраняется.
- Срок заявки today: если заявка создана (или сохранена правкой с deadline) меньше чем за час до конца суток по Москве, expires_at — 23:59:59 следующего дня. Раньше такая заявка закрывалась через минуты, а созданная в последнюю секунду суток сразу была просроченной. В остальное время today — до конца текущего дня, как раньше; days_2_3 и week не менялись. Форма кабинета теперь создаёт заявки со сроком days_2_3.
- «Принять» (POST /buyer/exchange/proposals/{id}/accept) без delivery.address по заявке с самовывозом (pickup: pickup или delivery.type: pickup): адрес заказа — пункт самовывоза площадки строкой «самовывоз, <адрес площадки>», как при оформлении корзины на сайте. Раньше в заказ записывался адрес компании покупателя; он остаётся, если адрес площадки не указан. Заказ продавцу отдаётся с тем же адресом.
- Покупателю в кабинете — уведомление, когда срок заявки истёк, а по ней были действующие КП или строки в корзине. Вебхук exchange.request.closed (reason expired) не менялся.
- 1.5.12026-10-10
- Заказ покупателя (GET /buyer/orders и GET /buyer/orders/{id}): documents.upd — пустая строка, пока заказ не отгружен (статус shipped, closed или return), даже если продавец выписал УПД заранее. Раньше номер УПД, выписанного до отгрузки, покупатель видел сразу. В кабинете покупателя правило то же: УПД появляется после отгрузки.
- Заказ покупателя: documents.ksf — пустая строка, пока по заказу нет согласованного возврата (одобренный возврат товара или возврат денег по претензии), а также у заказа без НДС и у покупателя-физлица. После согласования — номер корректировочного счёта-фактуры заказа: «КСФ-<номер заказа>-1» при одном согласованном возврате, сводный «КСФ-<номер заказа>» при нескольких. Раньше поле показывало номер, который можно было «выписать» по любому заказу.
- Заказ продавца (GET /seller/orders, GET /seller/orders/{id}): documents.upd — как раньше (продавец выписывает УПД и до отгрузки); documents.ksf записывается при согласовании возврата тем же номером, что видит покупатель. Формат полей не менялся.
- 1.5.02026-10-06
- «Принять» (POST /buyer/exchange/proposals/{id}/accept) без delivery.address: адрес доставки заказа — адрес получения заявки pickup_address; при самовывозе и пустом адресе заявки — адрес компании, как раньше. Раньше pickup_address в заказ не переносился.
- Программе продавца способ доставки заявки — только из справочника: значение вне справочника (так попадал адрес покупателя из классической формы кабинета) отдаётся как delivery = null и пустой delivery_name. Адрес покупателя продавец видит только в заказе — и в API, и в кабинете.
- Группа техники «Иное»: новое значение vehicle.vgroup = other (в справочнике /dictionary/exchange — последней, markets: false) — во входе и выдаче заявки, в параметре vgroup ленты продавца и в данных exchange.request.created. Программы с жёсткой проверкой перечисления должны принять новое значение. У other рынок не сохраняется.
- Заявка группы «Иное» проходит любой фильтр продавца: параметры vgroup и market ленты GET /seller/exchange/requests и фильтр подписки options.groups / options.markets. Легковые и грузовые без рынка (рынок «Иное», market = null) проходят любой фильтр market. options.groups = ["other"] — 400 VALIDATION_FAILED.
- Смена биржи заявки: POST /buyer/exchange/requests/{id}/update принимает exchange (new, used, all; раньше — 400). Расширить можно всегда — продавцам, которым заявка стала видна, уходит exchange.request.created. Сузить нельзя, пока у продавцов, которые потеряют доступ, есть действующие КП: 409 INVALID_STATE с details[0].field = exchange; черновики таких продавцов закрываются.
- Новые сроки строки КП: term = hours_3 (через 3 часа, term_days 0), tomorrow_am (завтра с утра, 1), week (через неделю, 7) — во входе POST /seller/exchange/requests/{id}/proposals, в ProposalLine у продавца и покупателя, в справочнике /dictionary/exchange (terms, по порядку списка формы). Прежние коды и подписи (in_stock, days_2_3 «2-3 дня», days_4_5 «4-5 дней», on_order) не изменились; без term код из term_days выводится, как раньше. Программы с жёсткой проверкой перечисления должны принять новые значения.
- availability строки КП: in_stock — только у term = in_stock (у other — при term_days = 0). Раньше in_stock давал и term_days = 0 при любом коде; hours_3 (0 дней) — on_order.
- Артикул и оригинальный номер продавца покупателю до покупки не раскрываются: в КП программе покупателя (GET /buyer/exchange/proposals и /proposals/{id}, ответ reject) article и oem — пустые строки, тип полей прежний. После заказа оба есть в строках заказа (ответ accept, GET /buyer/orders) и в УПД: новое поле oem в строке заказа покупателя (BuyerOrderLine и строки заказа ответа accept) — оригинальный номер строки КП, у строк витрины — пустая строка. Программе продавца его КП — как раньше.
- 1.4.22026-09-28
- Пределы песочницы на компанию: в тестовом заказе покупателя — не больше 20 позиций (400 VALIDATION_FAILED); тестовых заказов — не больше 50 действующих (409 LIMIT_EXCEEDED) и 200 за сутки (429 RATE_LIMITED с Retry-After); тестовых заявок биржи — не больше 100 за сутки (429 RATE_LIMITED). Тестовые заказы удаляются через exchangeTestRetentionDays (7 дней) после создания.
- Новая причина остановки загрузки held_reason: EMAIL_SENDER_UNVERIFIED — письмо в источник «почта» без указанного отправителя, отправитель не подтверждён (DMARC не pass); value и limit — null. Загрузка ждёт утверждения, как при других причинах.
- 1.4.12026-09-23
- Склад в строках заказа покупателя — город и обезличенный код warehouse_code («ТТ-014-2»); текст склада продавца покупателю не отдаётся.
- В текстах КП нельзя передавать и сведения о продавце: реквизиты, название, адрес, фамилии сотрудников — 422 CONTACTS_FORBIDDEN, в details[].kind — requisites, company или person; в старых записях — «[скрыто]».
- В текстах КП нельзя предлагать расчёты мимо площадки («платите напрямую продавцу», «минуя площадку», «переведите на карту») — 422 CONTACTS_FORBIDDEN, в details[].kind — bypass; в старых записях покупателю всё предложение показывается как «[скрыто]». Тексты КП покупателю очищаются с профилем продавца КП: его название, адрес складов и фамилии сотрудников скрыты и в старых записях.
- Заказы покупателя: экспресс через delivery.type: express, комментарий заказа comment до 500 символов, payment — код или подпись; accounting_total_rub — число (total_kop / 100).
- Idempotency-Key: прежний объект возвращается повтором, только пока он моложе срока хранения ключа (24 ч) и создан той же операцией; позже тот же ключ — новый запрос.
- ping: capabilities.exchange.enabled — включена ли биржа; заголовки X-RateLimit-* и в ответах 403, 404 и 405 с ключом; тело не в UTF-8 — 400 BAD_REQUEST.
- Вебхуки автовладельцу недоступны (403 FORBIDDEN_ROLE); в WebhookEnvelope.type перечислены события биржи.
- POST /buyer/orders тестовым ключом считается по классу лимита default (x-rate-limit-class-test): второй тестовый заказ подряд больше не получает 429; боевой ключ — bulk, как раньше.
- Описаны: mode поиска при одном бренде, адрес доставки заказа при «Принять» (delivery.address, без него — адрес компании; pickup_address заявки в заказ не переносится).
- 1.4.02026-09-23
- Биржа для программы покупателя-организации: создание, правка и снятие заявки, список и одна заявка, КП по заявкам, «Принять» целиком и по строкам, отклонение КП.
- Заказы покупателя: перепроверка условий POST /buyer/quotes, оформление POST /buyer/orders, список и один заказ (заказы магазина и из КП, фильтр source).
- Вебхуки покупателю: exchange.proposal.received, exchange.proposal.replaced, exchange.proposal.withdrawn, exchange.request.closed и order.* по его заказам.
- Песочница покупателя: sandbox.scenario в заявке (standard, two_sellers, withdraw, no_answer, expire) — роботы-продавцы отвечают КП; робот 5B подтверждает, отклоняет строку с qty = 13 и отгружает тестовые заказы.
- Каждое событие доставки описано отдельной записью в x-webhooks: назначение, кому приходит и пример конверта с data.
- Страница подключения public/api.html: разделы «Биржа: программа продавца», «Биржа: программа покупателя», «Заказы покупателя» и «Песочница биржи» с пошаговыми сценариями.
- 1.3.02026-09-22
- Биржа для программы продавца: лента заявок, КП и черновики, снятие КП, синхронизация по since_seq; справочник /dictionary/exchange.
- Право ключа exchange и регион заявок ключа (own, all); ключ отключённого сотрудника — 401 KEY_OWNER_INACTIVE.
- Заказы из КП в /seller/orders: source, exchange, actions, buyer.ref; confirm и reject — 409 EXCHANGE_ORDER; ship — как раньше.
- Вебхуки биржи exchange.request.* и exchange.proposal.*, шаблоны order.*, import.*, exchange.*, фильтр заявок options; «*» событий биржи не включает.
- Песочница биржи: POST /sandbox/exchange/requests — тестовая заявка робота-покупателя со сценарием.
- Юридические ворота: прайс, пакет предложений, утверждение загрузки и КП боевым ключом — после принятия документов площадки (403 LEGAL_NEEDS).
- 1.2.02026-09-17
- Вебхуки: подписка методами /webhooks и в кабинете, подпись HMAC, повторы 7 раз за сутки, проверочное событие ping.
- Код ошибки LIMIT_EXCEEDED (409) — предел подписок.
- В следующем обновлении: перепроверка цен и заказы покупателя через API.
- 1.1.02026-09-16
- Первая публикация API v1: прайсы файлом, пакет предложений, склады, загрузки и их проверка.
- Заказы продавца с подтверждением по строкам, отказом и отгрузкой; синхронизация по since_seq.
- Поиск покупателя, песочница с тестовыми ключами.
- В следующем обновлении: вебхуки, перепроверка цен и заказы покупателя через API.